• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2015 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/material.dart';
6
7import '../../gallery/demo.dart';
8
9const List<String> _defaultMaterials = <String>[
10  'poker',
11  'tortilla',
12  'fish and',
13  'micro',
14  'wood',
15];
16
17const List<String> _defaultActions = <String>[
18  'flake',
19  'cut',
20  'fragment',
21  'splinter',
22  'nick',
23  'fry',
24  'solder',
25  'cash in',
26  'eat',
27];
28
29const Map<String, String> _results = <String, String>{
30  'flake': 'flaking',
31  'cut': 'cutting',
32  'fragment': 'fragmenting',
33  'splinter': 'splintering',
34  'nick': 'nicking',
35  'fry': 'frying',
36  'solder': 'soldering',
37  'cash in': 'cashing in',
38  'eat': 'eating',
39};
40
41const List<String> _defaultTools = <String>[
42  'hammer',
43  'chisel',
44  'fryer',
45  'fabricator',
46  'customer',
47];
48
49const Map<String, String> _avatars = <String, String>{
50  'hammer': 'people/square/ali.png',
51  'chisel': 'people/square/sandra.png',
52  'fryer': 'people/square/trevor.png',
53  'fabricator': 'people/square/stella.png',
54  'customer': 'people/square/peter.png',
55};
56
57const Map<String, Set<String>> _toolActions = <String, Set<String>>{
58  'hammer': <String>{'flake', 'fragment', 'splinter'},
59  'chisel': <String>{'flake', 'nick', 'splinter'},
60  'fryer': <String>{'fry'},
61  'fabricator': <String>{'solder'},
62  'customer': <String>{'cash in', 'eat'},
63};
64
65const Map<String, Set<String>> _materialActions = <String, Set<String>>{
66  'poker': <String>{'cash in'},
67  'tortilla': <String>{'fry', 'eat'},
68  'fish and': <String>{'fry', 'eat'},
69  'micro': <String>{'solder', 'fragment'},
70  'wood': <String>{'flake', 'cut', 'splinter', 'nick'},
71};
72
73class _ChipsTile extends StatelessWidget {
74  const _ChipsTile({
75    Key key,
76    this.label,
77    this.children,
78  }) : super(key: key);
79
80  final String label;
81  final List<Widget> children;
82
83  // Wraps a list of chips into a ListTile for display as a section in the demo.
84  @override
85  Widget build(BuildContext context) {
86    final List<Widget> cardChildren = <Widget>[
87      Container(
88        padding: const EdgeInsets.only(top: 16.0, bottom: 4.0),
89        alignment: Alignment.center,
90        child: Text(label, textAlign: TextAlign.start),
91      ),
92    ];
93    if (children.isNotEmpty) {
94      cardChildren.add(Wrap(
95        children: children.map<Widget>((Widget chip) {
96          return Padding(
97            padding: const EdgeInsets.all(2.0),
98            child: chip,
99          );
100        }).toList()));
101    } else {
102      final TextStyle textStyle = Theme.of(context).textTheme.caption.copyWith(fontStyle: FontStyle.italic);
103      cardChildren.add(
104        Semantics(
105          container: true,
106          child: Container(
107            alignment: Alignment.center,
108            constraints: const BoxConstraints(minWidth: 48.0, minHeight: 48.0),
109            padding: const EdgeInsets.all(8.0),
110            child: Text('None', style: textStyle),
111          ),
112        ));
113    }
114
115    return Card(
116      semanticContainer: false,
117      child: Column(
118        mainAxisSize: MainAxisSize.min,
119        children: cardChildren,
120      ),
121    );
122  }
123}
124
125class ChipDemo extends StatefulWidget {
126  static const String routeName = '/material/chip';
127
128  @override
129  _ChipDemoState createState() => _ChipDemoState();
130}
131
132class _ChipDemoState extends State<ChipDemo> {
133  _ChipDemoState() {
134    _reset();
135  }
136
137  final Set<String> _materials = <String>{};
138  String _selectedMaterial = '';
139  String _selectedAction = '';
140  final Set<String> _tools = <String>{};
141  final Set<String> _selectedTools = <String>{};
142  final Set<String> _actions = <String>{};
143  bool _showShapeBorder = false;
144
145  // Initialize members with the default data.
146  void _reset() {
147    _materials.clear();
148    _materials.addAll(_defaultMaterials);
149    _actions.clear();
150    _actions.addAll(_defaultActions);
151    _tools.clear();
152    _tools.addAll(_defaultTools);
153    _selectedMaterial = '';
154    _selectedAction = '';
155    _selectedTools.clear();
156  }
157
158  void _removeMaterial(String name) {
159    _materials.remove(name);
160    if (_selectedMaterial == name) {
161      _selectedMaterial = '';
162    }
163  }
164
165  void _removeTool(String name) {
166    _tools.remove(name);
167    _selectedTools.remove(name);
168  }
169
170  String _capitalize(String name) {
171    assert(name != null && name.isNotEmpty);
172    return name.substring(0, 1).toUpperCase() + name.substring(1);
173  }
174
175  // This converts a String to a unique color, based on the hash value of the
176  // String object.  It takes the bottom 16 bits of the hash, and uses that to
177  // pick a hue for an HSV color, and then creates the color (with a preset
178  // saturation and value).  This means that any unique strings will also have
179  // unique colors, but they'll all be readable, since they have the same
180  // saturation and value.
181  Color _nameToColor(String name) {
182    assert(name.length > 1);
183    final int hash = name.hashCode & 0xffff;
184    final double hue = (360.0 * hash / (1 << 15)) % 360.0;
185    return HSVColor.fromAHSV(1.0, hue, 0.4, 0.90).toColor();
186  }
187
188  AssetImage _nameToAvatar(String name) {
189    assert(_avatars.containsKey(name));
190    return AssetImage(
191      _avatars[name],
192      package: 'flutter_gallery_assets',
193    );
194  }
195
196  String _createResult() {
197    if (_selectedAction.isEmpty) {
198      return '';
199    }
200    return _capitalize(_results[_selectedAction]) + '!';
201  }
202
203  @override
204  Widget build(BuildContext context) {
205    final List<Widget> chips = _materials.map<Widget>((String name) {
206      return Chip(
207        key: ValueKey<String>(name),
208        backgroundColor: _nameToColor(name),
209        label: Text(_capitalize(name)),
210        onDeleted: () {
211          setState(() {
212            _removeMaterial(name);
213          });
214        },
215      );
216    }).toList();
217
218    final List<Widget> inputChips = _tools.map<Widget>((String name) {
219      return InputChip(
220          key: ValueKey<String>(name),
221          avatar: CircleAvatar(
222            backgroundImage: _nameToAvatar(name),
223          ),
224          label: Text(_capitalize(name)),
225          onDeleted: () {
226            setState(() {
227              _removeTool(name);
228            });
229          });
230    }).toList();
231
232    final List<Widget> choiceChips = _materials.map<Widget>((String name) {
233      return ChoiceChip(
234        key: ValueKey<String>(name),
235        backgroundColor: _nameToColor(name),
236        label: Text(_capitalize(name)),
237        selected: _selectedMaterial == name,
238        onSelected: (bool value) {
239          setState(() {
240            _selectedMaterial = value ? name : '';
241          });
242        },
243      );
244    }).toList();
245
246    final List<Widget> filterChips = _defaultTools.map<Widget>((String name) {
247      return FilterChip(
248        key: ValueKey<String>(name),
249        label: Text(_capitalize(name)),
250        selected: _tools.contains(name) && _selectedTools.contains(name),
251        onSelected: !_tools.contains(name)
252            ? null
253            : (bool value) {
254                setState(() {
255                  if (!value) {
256                    _selectedTools.remove(name);
257                  } else {
258                    _selectedTools.add(name);
259                  }
260                });
261              },
262      );
263    }).toList();
264
265    Set<String> allowedActions = <String>{};
266    if (_selectedMaterial != null && _selectedMaterial.isNotEmpty) {
267      for (String tool in _selectedTools) {
268        allowedActions.addAll(_toolActions[tool]);
269      }
270      allowedActions = allowedActions.intersection(_materialActions[_selectedMaterial]);
271    }
272
273    final List<Widget> actionChips = allowedActions.map<Widget>((String name) {
274      return ActionChip(
275        label: Text(_capitalize(name)),
276        onPressed: () {
277          setState(() {
278            _selectedAction = name;
279          });
280        },
281      );
282    }).toList();
283
284    final ThemeData theme = Theme.of(context);
285    final List<Widget> tiles = <Widget>[
286      const SizedBox(height: 8.0, width: 0.0),
287      _ChipsTile(label: 'Available Materials (Chip)', children: chips),
288      _ChipsTile(label: 'Available Tools (InputChip)', children: inputChips),
289      _ChipsTile(label: 'Choose a Material (ChoiceChip)', children: choiceChips),
290      _ChipsTile(label: 'Choose Tools (FilterChip)', children: filterChips),
291      _ChipsTile(label: 'Perform Allowed Action (ActionChip)', children: actionChips),
292      const Divider(),
293      Padding(
294        padding: const EdgeInsets.all(8.0),
295        child: Center(
296          child: Text(
297            _createResult(),
298            style: theme.textTheme.title,
299          ),
300        ),
301      ),
302    ];
303
304    return Scaffold(
305      appBar: AppBar(
306        title: const Text('Chips'),
307        actions: <Widget>[
308          MaterialDemoDocumentationButton(ChipDemo.routeName),
309          IconButton(
310            onPressed: () {
311              setState(() {
312                _showShapeBorder = !_showShapeBorder;
313              });
314            },
315            icon: const Icon(Icons.vignette, semanticLabel: 'Update border shape'),
316          ),
317        ],
318      ),
319      body: ChipTheme(
320        data: _showShapeBorder
321            ? theme.chipTheme.copyWith(
322                shape: BeveledRectangleBorder(
323                side: const BorderSide(width: 0.66, style: BorderStyle.solid, color: Colors.grey),
324                borderRadius: BorderRadius.circular(10.0),
325              ))
326            : theme.chipTheme,
327        child: Scrollbar(child: ListView(children: tiles)),
328      ),
329      floatingActionButton: FloatingActionButton(
330        onPressed: () => setState(_reset),
331        child: const Icon(Icons.refresh, semanticLabel: 'Reset chips'),
332      ),
333    );
334  }
335}
336