1 | // Copyright 2014 The Flutter Authors. All rights reserved. |
2 | // Use of this source code is governed by a BSD-style license that can be |
3 | // found in the LICENSE file. |
4 | |
5 | import 'package:flutter/material.dart'; |
6 | |
7 | /// Flutter code sample for [CustomMultiChildLayout]. |
8 | |
9 | void main() => runApp(const CustomMultiChildLayoutApp()); |
10 | |
11 | class CustomMultiChildLayoutApp extends StatelessWidget { |
12 | const CustomMultiChildLayoutApp({super.key}); |
13 | |
14 | @override |
15 | Widget build(BuildContext context) { |
16 | return const MaterialApp( |
17 | home: Directionality( |
18 | // TRY THIS: Try changing the direction here and hot-reloading to |
19 | // see the layout change. |
20 | textDirection: TextDirection.ltr, |
21 | child: Scaffold(body: CustomMultiChildLayoutExample()), |
22 | ), |
23 | ); |
24 | } |
25 | } |
26 | |
27 | /// Lays out the children in a cascade, where the top corner of the next child |
28 | /// is a little above (`overlap`) the lower end corner of the previous child. |
29 | /// |
30 | /// Will relayout if the text direction changes. |
31 | class _CascadeLayoutDelegate extends MultiChildLayoutDelegate { |
32 | _CascadeLayoutDelegate({ |
33 | required this.colors, |
34 | required this.overlap, |
35 | required this.textDirection, |
36 | }); |
37 | |
38 | final Map<String, Color> colors; |
39 | final double overlap; |
40 | final TextDirection textDirection; |
41 | |
42 | // Perform layout will be called when re-layout is needed. |
43 | @override |
44 | void performLayout(Size size) { |
45 | final double columnWidth = size.width / colors.length; |
46 | Offset childPosition = Offset.zero; |
47 | switch (textDirection) { |
48 | case TextDirection.rtl: |
49 | childPosition += Offset(size.width, 0); |
50 | case TextDirection.ltr: |
51 | break; |
52 | } |
53 | for (final String color in colors.keys) { |
54 | // layoutChild must be called exactly once for each child. |
55 | final Size currentSize = layoutChild( |
56 | color, |
57 | BoxConstraints(maxHeight: size.height, maxWidth: columnWidth), |
58 | ); |
59 | // positionChild must be called to change the position of a child from |
60 | // what it was in the previous layout. Each child starts at (0, 0) for the |
61 | // first layout. |
62 | switch (textDirection) { |
63 | case TextDirection.rtl: |
64 | positionChild(color, childPosition - Offset(currentSize.width, 0)); |
65 | childPosition += Offset(-currentSize.width, currentSize.height - overlap); |
66 | case TextDirection.ltr: |
67 | positionChild(color, childPosition); |
68 | childPosition += Offset(currentSize.width, currentSize.height - overlap); |
69 | } |
70 | } |
71 | } |
72 | |
73 | // shouldRelayout is called to see if the delegate has changed and requires a |
74 | // layout to occur. Should only return true if the delegate state itself |
75 | // changes: changes in the CustomMultiChildLayout attributes will |
76 | // automatically cause a relayout, like any other widget. |
77 | @override |
78 | bool shouldRelayout(_CascadeLayoutDelegate oldDelegate) { |
79 | return oldDelegate.textDirection != textDirection || oldDelegate.overlap != overlap; |
80 | } |
81 | } |
82 | |
83 | class CustomMultiChildLayoutExample extends StatelessWidget { |
84 | const CustomMultiChildLayoutExample({super.key}); |
85 | |
86 | static const Map<String, Color> _colors = <String, Color>{ |
87 | 'Red' : Colors.red, |
88 | 'Green' : Colors.green, |
89 | 'Blue' : Colors.blue, |
90 | 'Cyan' : Colors.cyan, |
91 | }; |
92 | |
93 | @override |
94 | Widget build(BuildContext context) { |
95 | return CustomMultiChildLayout( |
96 | delegate: _CascadeLayoutDelegate( |
97 | colors: _colors, |
98 | overlap: 30.0, |
99 | textDirection: Directionality.of(context), |
100 | ), |
101 | children: <Widget>[ |
102 | // Create all of the colored boxes in the colors map. |
103 | for (final MapEntry<String, Color> entry in _colors.entries) |
104 | // The "id" can be any Object, not just a String. |
105 | LayoutId( |
106 | id: entry.key, |
107 | child: Container( |
108 | color: entry.value, |
109 | width: 100.0, |
110 | height: 100.0, |
111 | alignment: Alignment.center, |
112 | child: Text(entry.key), |
113 | ), |
114 | ), |
115 | ], |
116 | ); |
117 | } |
118 | } |
119 | |