[dynamic_layouts] Add a staggered grid layout example (#2559)
diff --git a/packages/dynamic_layouts/example/lib/main.dart b/packages/dynamic_layouts/example/lib/main.dart
index 5221ae6..18a2c20 100644
--- a/packages/dynamic_layouts/example/lib/main.dart
+++ b/packages/dynamic_layouts/example/lib/main.dart
@@ -4,6 +4,7 @@
import 'package:flutter/material.dart';
+import 'staggered_layout_example.dart';
import 'wrap_layout_example.dart';
void main() {
@@ -28,7 +29,6 @@
}
/// The home page
-
class MyHomePage extends StatelessWidget {
/// The home page constructor.
const MyHomePage({super.key});
@@ -52,6 +52,16 @@
),
child: const Text('Wrap Demo'),
),
+ const SizedBox(height: 20),
+ ElevatedButton(
+ onPressed: () => Navigator.push(
+ context,
+ MaterialPageRoute<void>(
+ builder: (BuildContext context) => const StaggeredExample(),
+ ),
+ ),
+ child: const Text('Staggered Demo'),
+ ),
],
),
),
diff --git a/packages/dynamic_layouts/example/lib/staggered_layout_example.dart b/packages/dynamic_layouts/example/lib/staggered_layout_example.dart
new file mode 100644
index 0000000..95b3020
--- /dev/null
+++ b/packages/dynamic_layouts/example/lib/staggered_layout_example.dart
@@ -0,0 +1,87 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:dynamic_layouts/dynamic_layouts.dart';
+import 'package:flutter/material.dart';
+
+void main() {
+ runApp(const StaggeredExample());
+}
+
+/// A staggered layout example. Clicking the upper-right button will change
+/// between a grid with a fixed cross axis count and one with a main axis
+/// extent.
+class StaggeredExample extends StatefulWidget {
+ /// Creates a [StaggeredExample].
+ const StaggeredExample({super.key});
+
+ @override
+ State<StaggeredExample> createState() => _StaggeredExampleState();
+}
+
+class _StaggeredExampleState extends State<StaggeredExample> {
+ final List<Widget> children = List<Widget>.generate(
+ 50,
+ (int index) => _DynamicSizedTile(index: index),
+ );
+
+ bool fixedCrossAxisCount = true;
+
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ appBar: AppBar(
+ title: const Text('Staggered Layout Example'),
+ actions: <Widget>[
+ Padding(
+ padding: const EdgeInsets.only(right: 50.0),
+ child: TextButton(
+ onPressed: () {
+ setState(() {
+ fixedCrossAxisCount = !fixedCrossAxisCount;
+ });
+ },
+ child: Text(
+ fixedCrossAxisCount ? 'FIXED' : 'MAX',
+ style: const TextStyle(color: Colors.white),
+ ),
+ ),
+ ),
+ ],
+ ),
+ floatingActionButton: FloatingActionButton(
+ onPressed: () {
+ setState(() {
+ children.add(_DynamicSizedTile(index: children.length));
+ });
+ },
+ child: const Icon(Icons.plus_one),
+ ),
+ body: fixedCrossAxisCount
+ ? DynamicGridView.staggered(
+ crossAxisCount: 4,
+ children: <Widget>[...children],
+ )
+ : DynamicGridView.staggered(
+ maxCrossAxisExtent: 100,
+ children: <Widget>[...children],
+ ),
+ );
+ }
+}
+
+class _DynamicSizedTile extends StatelessWidget {
+ const _DynamicSizedTile({required this.index});
+
+ final int index;
+
+ @override
+ Widget build(BuildContext context) {
+ return Container(
+ height: index % 3 * 50 + 20,
+ color: Colors.amber[(index % 8 + 1) * 100],
+ child: Text('Index $index'),
+ );
+ }
+}
diff --git a/packages/dynamic_layouts/example/test/staggered_example_test.dart b/packages/dynamic_layouts/example/test/staggered_example_test.dart
new file mode 100644
index 0000000..1b61dc6
--- /dev/null
+++ b/packages/dynamic_layouts/example/test/staggered_example_test.dart
@@ -0,0 +1,32 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:example/staggered_layout_example.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_test/flutter_test.dart';
+
+void main() {
+ testWidgets('StaggeredExample lays out children correctly',
+ (WidgetTester tester) async {
+ tester.binding.window.physicalSizeTestValue = const Size(400, 200);
+ tester.binding.window.devicePixelRatioTestValue = 1.0;
+
+ await tester.pumpWidget(
+ const MaterialApp(
+ home: StaggeredExample(),
+ ),
+ );
+ await tester.pumpAndSettle();
+
+ expect(find.text('Index 0'), findsOneWidget);
+ expect(tester.getTopLeft(find.text('Index 0')), const Offset(0.0, 56.0));
+ expect(find.text('Index 8'), findsOneWidget);
+ expect(tester.getTopLeft(find.text('Index 8')), const Offset(100.0, 146.0));
+ expect(find.text('Index 10'), findsOneWidget);
+ expect(
+ tester.getTopLeft(find.text('Index 10')),
+ const Offset(200.0, 196.0),
+ );
+ });
+}