• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2018 The Chromium 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
5import 'package:flutter/foundation.dart';
6import 'package:flutter/material.dart';
7import 'package:flutter/rendering.dart';
8
9import '../../gallery/demo.dart';
10
11enum _ReorderableListType {
12  /// A list tile that contains a [CircleAvatar].
13  horizontalAvatar,
14
15  /// A list tile that contains a [CircleAvatar].
16  verticalAvatar,
17
18  /// A list tile that contains three lines of text and a checkbox.
19  threeLine,
20}
21
22class ReorderableListDemo extends StatefulWidget {
23  const ReorderableListDemo({ Key key }) : super(key: key);
24
25  static const String routeName = '/material/reorderable-list';
26
27  @override
28  _ListDemoState createState() => _ListDemoState();
29}
30
31class _ListItem {
32  _ListItem(this.value, this.checkState);
33
34  final String value;
35
36  bool checkState;
37}
38
39class _ListDemoState extends State<ReorderableListDemo> {
40  static final GlobalKey<ScaffoldState> scaffoldKey = GlobalKey<ScaffoldState>();
41
42  PersistentBottomSheetController<void> _bottomSheet;
43  _ReorderableListType _itemType = _ReorderableListType.threeLine;
44  bool _reverse = false;
45  bool _reverseSort = false;
46  final List<_ListItem> _items = <String>[
47    'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
48  ].map<_ListItem>((String item) => _ListItem(item, false)).toList();
49
50  void changeItemType(_ReorderableListType type) {
51    setState(() {
52      _itemType = type;
53    });
54    // Rebuild the bottom sheet to reflect the selected list view.
55    _bottomSheet?.setState(() {
56      // Trigger a rebuild.
57    });
58    // Close the bottom sheet to give the user a clear view of the list.
59    _bottomSheet?.close();
60  }
61
62  void changeReverse(bool newValue) {
63    setState(() {
64      _reverse = newValue;
65    });
66    // Rebuild the bottom sheet to reflect the selected list view.
67    _bottomSheet?.setState(() {
68      // Trigger a rebuild.
69    });
70    // Close the bottom sheet to give the user a clear view of the list.
71    _bottomSheet?.close();
72  }
73
74  void _showConfigurationSheet() {
75    setState(() {
76      _bottomSheet = scaffoldKey.currentState.showBottomSheet<void>((BuildContext bottomSheetContext) {
77        return DecoratedBox(
78          decoration: const BoxDecoration(
79            border: Border(top: BorderSide(color: Colors.black26)),
80          ),
81          child: ListView(
82            shrinkWrap: true,
83            primary: false,
84            children: <Widget>[
85              CheckboxListTile(
86                dense: true,
87                title: const Text('Reverse'),
88                value: _reverse,
89                onChanged: changeReverse,
90              ),
91              RadioListTile<_ReorderableListType>(
92                dense: true,
93                title: const Text('Horizontal Avatars'),
94                value: _ReorderableListType.horizontalAvatar,
95                groupValue: _itemType,
96                onChanged: changeItemType,
97              ),
98              RadioListTile<_ReorderableListType>(
99                dense: true,
100                title: const Text('Vertical Avatars'),
101                value: _ReorderableListType.verticalAvatar,
102                groupValue: _itemType,
103                onChanged: changeItemType,
104              ),
105              RadioListTile<_ReorderableListType>(
106                dense: true,
107                title: const Text('Three-line'),
108                value: _ReorderableListType.threeLine,
109                groupValue: _itemType,
110                onChanged: changeItemType,
111              ),
112            ],
113          ),
114        );
115      });
116
117      // Garbage collect the bottom sheet when it closes.
118      _bottomSheet.closed.whenComplete(() {
119        if (mounted) {
120          setState(() {
121            _bottomSheet = null;
122          });
123        }
124      });
125    });
126  }
127
128  Widget buildListTile(_ListItem item) {
129    const Widget secondary = Text(
130      'Even more additional list item information appears on line three.',
131    );
132    Widget listTile;
133    switch (_itemType) {
134      case _ReorderableListType.threeLine:
135        listTile = CheckboxListTile(
136          key: Key(item.value),
137          isThreeLine: true,
138          value: item.checkState ?? false,
139          onChanged: (bool newValue) {
140            setState(() {
141              item.checkState = newValue;
142            });
143          },
144          title: Text('This item represents ${item.value}.'),
145          subtitle: secondary,
146          secondary: const Icon(Icons.drag_handle),
147        );
148        break;
149      case _ReorderableListType.horizontalAvatar:
150      case _ReorderableListType.verticalAvatar:
151        listTile = Container(
152          key: Key(item.value),
153          height: 100.0,
154          width: 100.0,
155          child: CircleAvatar(child: Text(item.value),
156            backgroundColor: Colors.green,
157          ),
158        );
159        break;
160    }
161
162    return listTile;
163  }
164
165  void _onReorder(int oldIndex, int newIndex) {
166    setState(() {
167      if (newIndex > oldIndex) {
168        newIndex -= 1;
169      }
170      final _ListItem item = _items.removeAt(oldIndex);
171      _items.insert(newIndex, item);
172    });
173  }
174
175
176  @override
177  Widget build(BuildContext context) {
178    return Scaffold(
179      key: scaffoldKey,
180      appBar: AppBar(
181        title: const Text('Reorderable list'),
182        actions: <Widget>[
183          MaterialDemoDocumentationButton(ReorderableListDemo.routeName),
184          IconButton(
185            icon: const Icon(Icons.sort_by_alpha),
186            tooltip: 'Sort',
187            onPressed: () {
188              setState(() {
189                _reverseSort = !_reverseSort;
190                _items.sort((_ListItem a, _ListItem b) => _reverseSort ? b.value.compareTo(a.value) : a.value.compareTo(b.value));
191              });
192            },
193          ),
194          IconButton(
195            icon: Icon(
196              Theme.of(context).platform == TargetPlatform.iOS
197                  ? Icons.more_horiz
198                  : Icons.more_vert,
199            ),
200            tooltip: 'Show menu',
201            onPressed: _bottomSheet == null ? _showConfigurationSheet : null,
202          ),
203        ],
204      ),
205      body: Scrollbar(
206        child: ReorderableListView(
207          header: _itemType != _ReorderableListType.threeLine
208              ? Padding(
209                  padding: const EdgeInsets.all(8.0),
210                  child: Text('Header of the list', style: Theme.of(context).textTheme.headline))
211              : null,
212          onReorder: _onReorder,
213          reverse: _reverse,
214          scrollDirection: _itemType == _ReorderableListType.horizontalAvatar ? Axis.horizontal : Axis.vertical,
215          padding: const EdgeInsets.symmetric(vertical: 8.0),
216          children: _items.map<Widget>(buildListTile).toList(),
217        ),
218      ),
219    );
220  }
221}
222