component_screen.dart 66 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308
  1. // Copyright 2021 The Flutter team. 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. import 'package:flutter/material.dart';
  5. import 'package:flutter/services.dart';
  6. const rowDivider = SizedBox(width: 20);
  7. const colDivider = SizedBox(height: 10);
  8. const tinySpacing = 3.0;
  9. const smallSpacing = 10.0;
  10. const double cardWidth = 115;
  11. const double widthConstraint = 450;
  12. class FirstComponentList extends StatelessWidget {
  13. const FirstComponentList({
  14. super.key,
  15. required this.showNavBottomBar,
  16. required this.scaffoldKey,
  17. required this.showSecondList,
  18. });
  19. final bool showNavBottomBar;
  20. final GlobalKey<ScaffoldState> scaffoldKey;
  21. final bool showSecondList;
  22. @override
  23. Widget build(BuildContext context) {
  24. // Fully traverse this list before moving on.
  25. return FocusTraversalGroup(
  26. child: ListView(
  27. padding: showSecondList
  28. ? const EdgeInsetsDirectional.only(end: smallSpacing)
  29. : EdgeInsets.zero,
  30. children: [
  31. const Actions(),
  32. colDivider,
  33. const Communication(),
  34. colDivider,
  35. const Containment(),
  36. if (!showSecondList) ...[
  37. colDivider,
  38. Navigation(scaffoldKey: scaffoldKey),
  39. colDivider,
  40. const Selection(),
  41. colDivider,
  42. const TextInputs()
  43. ],
  44. ],
  45. ),
  46. );
  47. }
  48. }
  49. class SecondComponentList extends StatelessWidget {
  50. const SecondComponentList({
  51. super.key,
  52. required this.scaffoldKey,
  53. });
  54. final GlobalKey<ScaffoldState> scaffoldKey;
  55. @override
  56. Widget build(BuildContext context) {
  57. // Fully traverse this list before moving on.
  58. return FocusTraversalGroup(
  59. child: ListView(
  60. padding: const EdgeInsetsDirectional.only(end: smallSpacing),
  61. children: <Widget>[
  62. Navigation(scaffoldKey: scaffoldKey),
  63. colDivider,
  64. const Selection(),
  65. colDivider,
  66. const TextInputs(),
  67. ],
  68. ),
  69. );
  70. }
  71. }
  72. class Actions extends StatelessWidget {
  73. const Actions({super.key});
  74. @override
  75. Widget build(BuildContext context) {
  76. return const ComponentGroupDecoration(label: 'Actions', children: <Widget>[
  77. Buttons(),
  78. FloatingActionButtons(),
  79. IconToggleButtons(),
  80. SegmentedButtons(),
  81. ]);
  82. }
  83. }
  84. class Communication extends StatelessWidget {
  85. const Communication({super.key});
  86. @override
  87. Widget build(BuildContext context) {
  88. return const ComponentGroupDecoration(label: 'Communication', children: [
  89. NavigationBars(
  90. selectedIndex: 1,
  91. isExampleBar: true,
  92. isBadgeExample: true,
  93. ),
  94. ProgressIndicators(),
  95. SnackBarSection(),
  96. ]);
  97. }
  98. }
  99. class Containment extends StatelessWidget {
  100. const Containment({super.key});
  101. @override
  102. Widget build(BuildContext context) {
  103. return const ComponentGroupDecoration(label: 'Containment', children: [
  104. BottomSheetSection(),
  105. Cards(),
  106. Dialogs(),
  107. Dividers(),
  108. // TODO: Add Lists, https://github.com/flutter/flutter/issues/114006
  109. // TODO: Add Side sheets, https://github.com/flutter/flutter/issues/119328
  110. ]);
  111. }
  112. }
  113. class Navigation extends StatelessWidget {
  114. const Navigation({super.key, required this.scaffoldKey});
  115. final GlobalKey<ScaffoldState> scaffoldKey;
  116. @override
  117. Widget build(BuildContext context) {
  118. return ComponentGroupDecoration(label: 'Navigation', children: [
  119. const BottomAppBars(),
  120. const NavigationBars(
  121. selectedIndex: 0,
  122. isExampleBar: true,
  123. ),
  124. NavigationDrawers(scaffoldKey: scaffoldKey),
  125. const NavigationRails(),
  126. // TODO: Add Search https://github.com/flutter/flutter/issues/117483
  127. const Tabs(),
  128. const TopAppBars(),
  129. ]);
  130. }
  131. }
  132. class Selection extends StatelessWidget {
  133. const Selection({super.key});
  134. @override
  135. Widget build(BuildContext context) {
  136. return const ComponentGroupDecoration(label: 'Selection', children: [
  137. Checkboxes(),
  138. Chips(),
  139. // TODO: Add Date pickers https://github.com/flutter/flutter/issues/101481
  140. Menus(),
  141. Radios(),
  142. Sliders(),
  143. Switches(),
  144. // TODO: Add Time pickers https://github.com/flutter/flutter/issues/101480
  145. ]);
  146. }
  147. }
  148. class TextInputs extends StatelessWidget {
  149. const TextInputs({super.key});
  150. @override
  151. Widget build(BuildContext context) {
  152. return const ComponentGroupDecoration(
  153. label: 'Text inputs',
  154. children: [TextFields()],
  155. );
  156. }
  157. }
  158. class Buttons extends StatefulWidget {
  159. const Buttons({super.key});
  160. @override
  161. State<Buttons> createState() => _ButtonsState();
  162. }
  163. class _ButtonsState extends State<Buttons> {
  164. @override
  165. Widget build(BuildContext context) {
  166. return ComponentDecoration(
  167. label: 'Common buttons',
  168. tooltipMessage:
  169. 'Use ElevatedButton, FilledButton, FilledButton.tonal, OutlinedButton, or TextButton',
  170. child: SingleChildScrollView(
  171. scrollDirection: Axis.horizontal,
  172. child: Row(
  173. mainAxisAlignment: MainAxisAlignment.spaceAround,
  174. children: const <Widget>[
  175. ButtonsWithoutIcon(isDisabled: false),
  176. ButtonsWithIcon(),
  177. ButtonsWithoutIcon(isDisabled: true),
  178. ],
  179. ),
  180. ),
  181. );
  182. }
  183. }
  184. class ButtonsWithoutIcon extends StatelessWidget {
  185. final bool isDisabled;
  186. const ButtonsWithoutIcon({super.key, required this.isDisabled});
  187. @override
  188. Widget build(BuildContext context) {
  189. return Padding(
  190. padding: const EdgeInsets.symmetric(horizontal: 5.0),
  191. child: IntrinsicWidth(
  192. child: Column(
  193. crossAxisAlignment: CrossAxisAlignment.stretch,
  194. children: <Widget>[
  195. ElevatedButton(
  196. onPressed: isDisabled ? null : () {},
  197. child: const Text('Elevated'),
  198. ),
  199. colDivider,
  200. FilledButton(
  201. onPressed: isDisabled ? null : () {},
  202. child: const Text('Filled'),
  203. ),
  204. colDivider,
  205. FilledButton.tonal(
  206. onPressed: isDisabled ? null : () {},
  207. child: const Text('Filled tonal'),
  208. ),
  209. colDivider,
  210. OutlinedButton(
  211. onPressed: isDisabled ? null : () {},
  212. child: const Text('Outlined'),
  213. ),
  214. colDivider,
  215. TextButton(
  216. onPressed: isDisabled ? null : () {},
  217. child: const Text('Text'),
  218. ),
  219. ],
  220. ),
  221. ),
  222. );
  223. }
  224. }
  225. class ButtonsWithIcon extends StatelessWidget {
  226. const ButtonsWithIcon({super.key});
  227. @override
  228. Widget build(BuildContext context) {
  229. return Padding(
  230. padding: const EdgeInsets.symmetric(horizontal: 10.0),
  231. child: IntrinsicWidth(
  232. child: Column(
  233. crossAxisAlignment: CrossAxisAlignment.stretch,
  234. children: <Widget>[
  235. ElevatedButton.icon(
  236. onPressed: () {},
  237. icon: const Icon(Icons.add),
  238. label: const Text('Icon'),
  239. ),
  240. colDivider,
  241. FilledButton.icon(
  242. onPressed: () {},
  243. label: const Text('Icon'),
  244. icon: const Icon(Icons.add),
  245. ),
  246. colDivider,
  247. FilledButton.tonalIcon(
  248. onPressed: () {},
  249. label: const Text('Icon'),
  250. icon: const Icon(Icons.add),
  251. ),
  252. colDivider,
  253. OutlinedButton.icon(
  254. onPressed: () {},
  255. icon: const Icon(Icons.add),
  256. label: const Text('Icon'),
  257. ),
  258. colDivider,
  259. TextButton.icon(
  260. onPressed: () {},
  261. icon: const Icon(Icons.add),
  262. label: const Text('Icon'),
  263. )
  264. ],
  265. ),
  266. ),
  267. );
  268. }
  269. }
  270. class FloatingActionButtons extends StatelessWidget {
  271. const FloatingActionButtons({super.key});
  272. @override
  273. Widget build(BuildContext context) {
  274. return ComponentDecoration(
  275. label: 'Floating action buttons',
  276. tooltipMessage:
  277. 'Use FloatingActionButton or FloatingActionButton.extended',
  278. child: Wrap(
  279. crossAxisAlignment: WrapCrossAlignment.center,
  280. runSpacing: smallSpacing,
  281. spacing: smallSpacing,
  282. children: [
  283. FloatingActionButton.small(
  284. onPressed: () {},
  285. tooltip: 'Small',
  286. child: const Icon(Icons.add),
  287. ),
  288. FloatingActionButton.extended(
  289. onPressed: () {},
  290. tooltip: 'Extended',
  291. icon: const Icon(Icons.add),
  292. label: const Text('Create'),
  293. ),
  294. FloatingActionButton(
  295. onPressed: () {},
  296. tooltip: 'Standard',
  297. child: const Icon(Icons.add),
  298. ),
  299. FloatingActionButton.large(
  300. onPressed: () {},
  301. tooltip: 'Large',
  302. child: const Icon(Icons.add),
  303. ),
  304. ],
  305. ),
  306. );
  307. }
  308. }
  309. class Cards extends StatelessWidget {
  310. const Cards({super.key});
  311. @override
  312. Widget build(BuildContext context) {
  313. return ComponentDecoration(
  314. label: 'Cards',
  315. tooltipMessage: 'Use Card',
  316. child: Wrap(
  317. alignment: WrapAlignment.spaceEvenly,
  318. children: [
  319. SizedBox(
  320. width: cardWidth,
  321. child: Card(
  322. child: Container(
  323. padding: const EdgeInsets.fromLTRB(10, 5, 5, 10),
  324. child: Column(
  325. children: [
  326. Align(
  327. alignment: Alignment.topRight,
  328. child: IconButton(
  329. icon: const Icon(Icons.more_vert),
  330. onPressed: () {},
  331. ),
  332. ),
  333. const SizedBox(height: 20),
  334. const Align(
  335. alignment: Alignment.bottomLeft,
  336. child: Text('Elevated'),
  337. )
  338. ],
  339. ),
  340. ),
  341. ),
  342. ),
  343. SizedBox(
  344. width: cardWidth,
  345. child: Card(
  346. color: Theme.of(context).colorScheme.surfaceVariant,
  347. elevation: 0,
  348. child: Container(
  349. padding: const EdgeInsets.fromLTRB(10, 5, 5, 10),
  350. child: Column(
  351. children: [
  352. Align(
  353. alignment: Alignment.topRight,
  354. child: IconButton(
  355. icon: const Icon(Icons.more_vert),
  356. onPressed: () {},
  357. ),
  358. ),
  359. const SizedBox(height: 20),
  360. const Align(
  361. alignment: Alignment.bottomLeft,
  362. child: Text('Filled'),
  363. )
  364. ],
  365. ),
  366. ),
  367. ),
  368. ),
  369. SizedBox(
  370. width: cardWidth,
  371. child: Card(
  372. elevation: 0,
  373. shape: RoundedRectangleBorder(
  374. side: BorderSide(
  375. color: Theme.of(context).colorScheme.outline,
  376. ),
  377. borderRadius: const BorderRadius.all(Radius.circular(12)),
  378. ),
  379. child: Container(
  380. padding: const EdgeInsets.fromLTRB(10, 5, 5, 10),
  381. child: Column(
  382. children: [
  383. Align(
  384. alignment: Alignment.topRight,
  385. child: IconButton(
  386. icon: const Icon(Icons.more_vert),
  387. onPressed: () {},
  388. ),
  389. ),
  390. const SizedBox(height: 20),
  391. const Align(
  392. alignment: Alignment.bottomLeft,
  393. child: Text('Outlined'),
  394. )
  395. ],
  396. ),
  397. ),
  398. ),
  399. ),
  400. ],
  401. ),
  402. );
  403. }
  404. }
  405. class _ClearButton extends StatelessWidget {
  406. const _ClearButton({required this.controller});
  407. final TextEditingController controller;
  408. @override
  409. Widget build(BuildContext context) => IconButton(
  410. icon: const Icon(Icons.clear),
  411. onPressed: () => controller.clear(),
  412. );
  413. }
  414. class TextFields extends StatefulWidget {
  415. const TextFields({super.key});
  416. @override
  417. State<TextFields> createState() => _TextFieldsState();
  418. }
  419. class _TextFieldsState extends State<TextFields> {
  420. final TextEditingController _controllerFilled = TextEditingController();
  421. final TextEditingController _controllerOutlined = TextEditingController();
  422. @override
  423. Widget build(BuildContext context) {
  424. return ComponentDecoration(
  425. label: 'Text fields',
  426. tooltipMessage: 'Use TextField with different InputDecoration',
  427. child: Column(
  428. mainAxisAlignment: MainAxisAlignment.start,
  429. children: [
  430. Padding(
  431. padding: const EdgeInsets.all(smallSpacing),
  432. child: TextField(
  433. controller: _controllerFilled,
  434. decoration: InputDecoration(
  435. prefixIcon: const Icon(Icons.search),
  436. suffixIcon: _ClearButton(controller: _controllerFilled),
  437. labelText: 'Filled',
  438. hintText: 'hint text',
  439. helperText: 'supporting text',
  440. filled: true,
  441. ),
  442. ),
  443. ),
  444. Padding(
  445. padding: const EdgeInsets.all(smallSpacing),
  446. child: Row(
  447. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  448. children: [
  449. Flexible(
  450. child: SizedBox(
  451. width: 200,
  452. child: TextField(
  453. maxLength: 10,
  454. maxLengthEnforcement: MaxLengthEnforcement.none,
  455. controller: _controllerFilled,
  456. decoration: InputDecoration(
  457. prefixIcon: const Icon(Icons.search),
  458. suffixIcon: _ClearButton(controller: _controllerFilled),
  459. labelText: 'Filled',
  460. hintText: 'hint text',
  461. helperText: 'supporting text',
  462. filled: true,
  463. errorText: 'error text',
  464. ),
  465. ),
  466. ),
  467. ),
  468. const SizedBox(width: smallSpacing),
  469. Flexible(
  470. child: SizedBox(
  471. width: 200,
  472. child: TextField(
  473. controller: _controllerFilled,
  474. enabled: false,
  475. decoration: InputDecoration(
  476. prefixIcon: const Icon(Icons.search),
  477. suffixIcon: _ClearButton(controller: _controllerFilled),
  478. labelText: 'Disabled',
  479. hintText: 'hint text',
  480. helperText: 'supporting text',
  481. filled: true,
  482. ),
  483. ),
  484. ),
  485. ),
  486. ],
  487. ),
  488. ),
  489. Padding(
  490. padding: const EdgeInsets.all(smallSpacing),
  491. child: TextField(
  492. controller: _controllerOutlined,
  493. decoration: InputDecoration(
  494. prefixIcon: const Icon(Icons.search),
  495. suffixIcon: _ClearButton(controller: _controllerOutlined),
  496. labelText: 'Outlined',
  497. hintText: 'hint text',
  498. helperText: 'supporting text',
  499. border: const OutlineInputBorder(),
  500. ),
  501. ),
  502. ),
  503. Padding(
  504. padding: const EdgeInsets.all(smallSpacing),
  505. child: Row(
  506. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  507. children: [
  508. Flexible(
  509. child: SizedBox(
  510. width: 200,
  511. child: TextField(
  512. controller: _controllerOutlined,
  513. decoration: InputDecoration(
  514. prefixIcon: const Icon(Icons.search),
  515. suffixIcon:
  516. _ClearButton(controller: _controllerOutlined),
  517. labelText: 'Outlined',
  518. hintText: 'hint text',
  519. helperText: 'supporting text',
  520. errorText: 'error text',
  521. border: const OutlineInputBorder(),
  522. filled: true,
  523. ),
  524. ),
  525. ),
  526. ),
  527. const SizedBox(width: smallSpacing),
  528. Flexible(
  529. child: SizedBox(
  530. width: 200,
  531. child: TextField(
  532. controller: _controllerOutlined,
  533. enabled: false,
  534. decoration: InputDecoration(
  535. prefixIcon: const Icon(Icons.search),
  536. suffixIcon:
  537. _ClearButton(controller: _controllerOutlined),
  538. labelText: 'Disabled',
  539. hintText: 'hint text',
  540. helperText: 'supporting text',
  541. border: const OutlineInputBorder(),
  542. filled: true,
  543. ),
  544. ),
  545. ),
  546. ),
  547. ])),
  548. ],
  549. ),
  550. );
  551. }
  552. }
  553. class Dialogs extends StatefulWidget {
  554. const Dialogs({super.key});
  555. @override
  556. State<Dialogs> createState() => _DialogsState();
  557. }
  558. class _DialogsState extends State<Dialogs> {
  559. void openDialog(BuildContext context) {
  560. showDialog<void>(
  561. context: context,
  562. builder: (context) => AlertDialog(
  563. title: const Text('What is a dialog?'),
  564. content: const Text(
  565. 'A dialog is a type of modal window that appears in front of app content to provide critical information, or prompt for a decision to be made.'),
  566. actions: <Widget>[
  567. TextButton(
  568. child: const Text('Okay'),
  569. onPressed: () => Navigator.of(context).pop(),
  570. ),
  571. FilledButton(
  572. child: const Text('Dismiss'),
  573. onPressed: () => Navigator.of(context).pop(),
  574. ),
  575. ],
  576. ),
  577. );
  578. }
  579. void openFullscreenDialog(BuildContext context) {
  580. showDialog<void>(
  581. context: context,
  582. builder: (context) => Dialog.fullscreen(
  583. child: Padding(
  584. padding: const EdgeInsets.all(20.0),
  585. child: Scaffold(
  586. appBar: AppBar(
  587. title: const Text('Full-screen dialog'),
  588. centerTitle: false,
  589. leading: IconButton(
  590. icon: const Icon(Icons.close),
  591. onPressed: () => Navigator.of(context).pop(),
  592. ),
  593. actions: [
  594. TextButton(
  595. child: const Text('Close'),
  596. onPressed: () => Navigator.of(context).pop(),
  597. ),
  598. ],
  599. ),
  600. ),
  601. ),
  602. ),
  603. );
  604. }
  605. @override
  606. Widget build(BuildContext context) {
  607. return ComponentDecoration(
  608. label: 'Dialog',
  609. tooltipMessage:
  610. 'Use showDialog with Dialog.fullscreen, AlertDialog, or SimpleDialog',
  611. child: Wrap(
  612. alignment: WrapAlignment.spaceBetween,
  613. children: [
  614. TextButton(
  615. child: const Text(
  616. 'Show dialog',
  617. style: TextStyle(fontWeight: FontWeight.bold),
  618. ),
  619. onPressed: () => openDialog(context),
  620. ),
  621. TextButton(
  622. child: const Text(
  623. 'Show full-screen dialog',
  624. style: TextStyle(fontWeight: FontWeight.bold),
  625. ),
  626. onPressed: () => openFullscreenDialog(context),
  627. ),
  628. ],
  629. ),
  630. );
  631. }
  632. }
  633. class Dividers extends StatelessWidget {
  634. const Dividers({super.key});
  635. @override
  636. Widget build(BuildContext context) {
  637. return ComponentDecoration(
  638. label: 'Dividers',
  639. tooltipMessage: 'Use Divider or VerticalDivider',
  640. child: Column(
  641. children: const <Widget>[
  642. Divider(key: Key('divider')),
  643. ],
  644. ),
  645. );
  646. }
  647. }
  648. class Switches extends StatelessWidget {
  649. const Switches({super.key});
  650. @override
  651. Widget build(BuildContext context) {
  652. return ComponentDecoration(
  653. label: 'Switches',
  654. tooltipMessage: 'Use SwitchListTile or Switch',
  655. child: Column(
  656. children: const <Widget>[
  657. SwitchRow(isEnabled: true),
  658. SwitchRow(isEnabled: false),
  659. ],
  660. ),
  661. );
  662. }
  663. }
  664. class SwitchRow extends StatefulWidget {
  665. const SwitchRow({super.key, required this.isEnabled});
  666. final bool isEnabled;
  667. @override
  668. State<SwitchRow> createState() => _SwitchRowState();
  669. }
  670. class _SwitchRowState extends State<SwitchRow> {
  671. bool value0 = false;
  672. bool value1 = true;
  673. final MaterialStateProperty<Icon?> thumbIcon =
  674. MaterialStateProperty.resolveWith<Icon?>((states) {
  675. if (states.contains(MaterialState.selected)) {
  676. return const Icon(Icons.check);
  677. }
  678. return const Icon(Icons.close);
  679. });
  680. @override
  681. Widget build(BuildContext context) {
  682. return Row(
  683. mainAxisAlignment: MainAxisAlignment.spaceEvenly,
  684. children: <Widget>[
  685. // TODO: use SwitchListTile when thumbIcon is available https://github.com/flutter/flutter/issues/118616
  686. Switch(
  687. value: value0,
  688. onChanged: widget.isEnabled
  689. ? (value) {
  690. setState(() {
  691. value0 = value;
  692. });
  693. }
  694. : null,
  695. ),
  696. Switch(
  697. thumbIcon: thumbIcon,
  698. value: value1,
  699. onChanged: widget.isEnabled
  700. ? (value) {
  701. setState(() {
  702. value1 = value;
  703. });
  704. }
  705. : null,
  706. ),
  707. ],
  708. );
  709. }
  710. }
  711. class Checkboxes extends StatefulWidget {
  712. const Checkboxes({super.key});
  713. @override
  714. State<Checkboxes> createState() => _CheckboxesState();
  715. }
  716. class _CheckboxesState extends State<Checkboxes> {
  717. bool? isChecked0 = true;
  718. bool? isChecked1;
  719. bool? isChecked2 = false;
  720. @override
  721. Widget build(BuildContext context) {
  722. return ComponentDecoration(
  723. label: 'Checkboxes',
  724. tooltipMessage: 'Use CheckboxListTile or Checkbox',
  725. child: Column(
  726. children: <Widget>[
  727. CheckboxListTile(
  728. tristate: true,
  729. value: isChecked0,
  730. title: const Text('Option 1'),
  731. onChanged: (value) {
  732. setState(() {
  733. isChecked0 = value;
  734. });
  735. },
  736. ),
  737. CheckboxListTile(
  738. tristate: true,
  739. value: isChecked1,
  740. title: const Text('Option 2'),
  741. onChanged: (value) {
  742. setState(() {
  743. isChecked1 = value;
  744. });
  745. },
  746. ),
  747. CheckboxListTile(
  748. tristate: true,
  749. value: isChecked2,
  750. title: const Text('Option 3'),
  751. // TODO: showcase error state https://github.com/flutter/flutter/issues/118616
  752. onChanged: (value) {
  753. setState(() {
  754. isChecked2 = value;
  755. });
  756. },
  757. ),
  758. const CheckboxListTile(
  759. tristate: true,
  760. title: Text('Option 4'),
  761. value: true,
  762. onChanged: null,
  763. ),
  764. ],
  765. ),
  766. );
  767. }
  768. }
  769. enum Value { first, second }
  770. class Radios extends StatefulWidget {
  771. const Radios({super.key});
  772. @override
  773. State<Radios> createState() => _RadiosState();
  774. }
  775. enum Options { option1, option2, option3 }
  776. class _RadiosState extends State<Radios> {
  777. Options? _selectedOption = Options.option1;
  778. @override
  779. Widget build(BuildContext context) {
  780. return ComponentDecoration(
  781. label: 'Radio buttons',
  782. tooltipMessage: 'Use RadioListTile<T> or Radio<T>',
  783. child: Column(
  784. children: <Widget>[
  785. RadioListTile<Options>(
  786. title: const Text('Option 1'),
  787. value: Options.option1,
  788. groupValue: _selectedOption,
  789. onChanged: (value) {
  790. setState(() {
  791. _selectedOption = value;
  792. });
  793. },
  794. ),
  795. RadioListTile<Options>(
  796. title: const Text('Option 2'),
  797. value: Options.option2,
  798. groupValue: _selectedOption,
  799. onChanged: (value) {
  800. setState(() {
  801. _selectedOption = value;
  802. });
  803. },
  804. ),
  805. RadioListTile<Options>(
  806. title: const Text('Option 3'),
  807. value: Options.option3,
  808. groupValue: _selectedOption,
  809. onChanged: null,
  810. ),
  811. ],
  812. ),
  813. );
  814. }
  815. }
  816. class ProgressIndicators extends StatefulWidget {
  817. const ProgressIndicators({super.key});
  818. @override
  819. State<ProgressIndicators> createState() => _ProgressIndicatorsState();
  820. }
  821. class _ProgressIndicatorsState extends State<ProgressIndicators> {
  822. bool playProgressIndicator = false;
  823. @override
  824. Widget build(BuildContext context) {
  825. final double? progressValue = playProgressIndicator ? null : 0.7;
  826. return ComponentDecoration(
  827. label: 'Progress indicators',
  828. tooltipMessage:
  829. 'Use CircularProgressIndicator or LinearProgressIndicator',
  830. child: Column(
  831. children: <Widget>[
  832. Row(
  833. children: [
  834. IconButton(
  835. isSelected: playProgressIndicator,
  836. selectedIcon: const Icon(Icons.pause),
  837. icon: const Icon(Icons.play_arrow),
  838. onPressed: () {
  839. setState(() {
  840. playProgressIndicator = !playProgressIndicator;
  841. });
  842. },
  843. ),
  844. Expanded(
  845. child: Row(
  846. children: <Widget>[
  847. rowDivider,
  848. CircularProgressIndicator(
  849. value: progressValue,
  850. ),
  851. rowDivider,
  852. Expanded(
  853. child: LinearProgressIndicator(
  854. value: progressValue,
  855. ),
  856. ),
  857. rowDivider,
  858. ],
  859. ),
  860. ),
  861. ],
  862. ),
  863. ],
  864. ),
  865. );
  866. }
  867. }
  868. const List<NavigationDestination> appBarDestinations = [
  869. NavigationDestination(
  870. tooltip: '',
  871. icon: Icon(Icons.widgets_outlined),
  872. label: 'Components',
  873. selectedIcon: Icon(Icons.widgets),
  874. ),
  875. NavigationDestination(
  876. tooltip: '',
  877. icon: Icon(Icons.format_paint_outlined),
  878. label: 'Color',
  879. selectedIcon: Icon(Icons.format_paint),
  880. ),
  881. NavigationDestination(
  882. tooltip: '',
  883. icon: Icon(Icons.text_snippet_outlined),
  884. label: 'Typography',
  885. selectedIcon: Icon(Icons.text_snippet),
  886. ),
  887. NavigationDestination(
  888. tooltip: '',
  889. icon: Icon(Icons.invert_colors_on_outlined),
  890. label: 'Elevation',
  891. selectedIcon: Icon(Icons.opacity),
  892. )
  893. ];
  894. const List<Widget> exampleBarDestinations = [
  895. NavigationDestination(
  896. tooltip: '',
  897. icon: Icon(Icons.explore_outlined),
  898. label: 'Explore',
  899. selectedIcon: Icon(Icons.explore),
  900. ),
  901. NavigationDestination(
  902. tooltip: '',
  903. icon: Icon(Icons.pets_outlined),
  904. label: 'Pets',
  905. selectedIcon: Icon(Icons.pets),
  906. ),
  907. NavigationDestination(
  908. tooltip: '',
  909. icon: Icon(Icons.account_box_outlined),
  910. label: 'Account',
  911. selectedIcon: Icon(Icons.account_box),
  912. )
  913. ];
  914. List<Widget> barWithBadgeDestinations = [
  915. NavigationDestination(
  916. tooltip: '',
  917. icon: Badge.count(count: 1000, child: const Icon(Icons.mail_outlined)),
  918. label: 'Mail',
  919. selectedIcon: Badge.count(count: 1000, child: const Icon(Icons.mail)),
  920. ),
  921. const NavigationDestination(
  922. tooltip: '',
  923. icon: Badge(label: Text('10'), child: Icon(Icons.chat_bubble_outline)),
  924. label: 'Chat',
  925. selectedIcon: Badge(label: Text('10'), child: Icon(Icons.chat_bubble)),
  926. ),
  927. const NavigationDestination(
  928. tooltip: '',
  929. icon: Badge(child: Icon(Icons.group_outlined)),
  930. label: 'Rooms',
  931. selectedIcon: Badge(child: Icon(Icons.group_rounded)),
  932. ),
  933. NavigationDestination(
  934. tooltip: '',
  935. icon: Badge.count(count: 3, child: const Icon(Icons.videocam_outlined)),
  936. label: 'Meet',
  937. selectedIcon: Badge.count(count: 3, child: const Icon(Icons.videocam)),
  938. )
  939. ];
  940. class NavigationBars extends StatefulWidget {
  941. const NavigationBars({
  942. super.key,
  943. this.onSelectItem,
  944. required this.selectedIndex,
  945. required this.isExampleBar,
  946. this.isBadgeExample = false,
  947. });
  948. final void Function(int)? onSelectItem;
  949. final int selectedIndex;
  950. final bool isExampleBar;
  951. final bool isBadgeExample;
  952. @override
  953. State<NavigationBars> createState() => _NavigationBarsState();
  954. }
  955. class _NavigationBarsState extends State<NavigationBars> {
  956. late int selectedIndex;
  957. @override
  958. void initState() {
  959. super.initState();
  960. selectedIndex = widget.selectedIndex;
  961. }
  962. @override
  963. void didUpdateWidget(covariant NavigationBars oldWidget) {
  964. super.didUpdateWidget(oldWidget);
  965. if (widget.selectedIndex != oldWidget.selectedIndex) {
  966. selectedIndex = widget.selectedIndex;
  967. }
  968. }
  969. @override
  970. Widget build(BuildContext context) {
  971. // App NavigationBar should get first focus.
  972. Widget navigationBar = Focus(
  973. autofocus: !(widget.isExampleBar || widget.isBadgeExample),
  974. child: NavigationBar(
  975. selectedIndex: selectedIndex,
  976. onDestinationSelected: (index) {
  977. setState(() {
  978. selectedIndex = index;
  979. });
  980. if (!widget.isExampleBar) widget.onSelectItem!(index);
  981. },
  982. destinations: widget.isExampleBar && widget.isBadgeExample
  983. ? barWithBadgeDestinations
  984. : widget.isExampleBar
  985. ? exampleBarDestinations
  986. : appBarDestinations,
  987. ),
  988. );
  989. if (widget.isExampleBar && widget.isBadgeExample) {
  990. navigationBar = ComponentDecoration(
  991. label: 'Badges',
  992. tooltipMessage: 'Use Badge or Badge.count',
  993. child: navigationBar);
  994. } else if (widget.isExampleBar) {
  995. navigationBar = ComponentDecoration(
  996. label: 'Navigation bar',
  997. tooltipMessage: 'Use NavigationBar',
  998. child: navigationBar);
  999. }
  1000. return navigationBar;
  1001. }
  1002. }
  1003. class IconToggleButtons extends StatefulWidget {
  1004. const IconToggleButtons({super.key});
  1005. @override
  1006. State<IconToggleButtons> createState() => _IconToggleButtonsState();
  1007. }
  1008. class _IconToggleButtonsState extends State<IconToggleButtons> {
  1009. @override
  1010. Widget build(BuildContext context) {
  1011. return ComponentDecoration(
  1012. label: 'Icon buttons',
  1013. tooltipMessage: 'Use IconButton',
  1014. child: Row(
  1015. mainAxisAlignment: MainAxisAlignment.spaceAround,
  1016. children: <Widget>[
  1017. Column(
  1018. // Standard IconButton
  1019. children: const <Widget>[
  1020. IconToggleButton(
  1021. isEnabled: true,
  1022. tooltip: 'Standard',
  1023. ),
  1024. colDivider,
  1025. IconToggleButton(
  1026. isEnabled: false,
  1027. tooltip: 'Standard (disabled)',
  1028. ),
  1029. ],
  1030. ),
  1031. Column(
  1032. children: const <Widget>[
  1033. // Filled IconButton
  1034. IconToggleButton(
  1035. isEnabled: true,
  1036. tooltip: 'Filled',
  1037. getDefaultStyle: enabledFilledButtonStyle,
  1038. ),
  1039. colDivider,
  1040. IconToggleButton(
  1041. isEnabled: false,
  1042. tooltip: 'Filled (disabled)',
  1043. getDefaultStyle: disabledFilledButtonStyle,
  1044. ),
  1045. ],
  1046. ),
  1047. Column(
  1048. children: const <Widget>[
  1049. // Filled Tonal IconButton
  1050. IconToggleButton(
  1051. isEnabled: true,
  1052. tooltip: 'Filled tonal',
  1053. getDefaultStyle: enabledFilledTonalButtonStyle,
  1054. ),
  1055. colDivider,
  1056. IconToggleButton(
  1057. isEnabled: false,
  1058. tooltip: 'Filled tonal (disabled)',
  1059. getDefaultStyle: disabledFilledTonalButtonStyle,
  1060. ),
  1061. ],
  1062. ),
  1063. Column(
  1064. children: const <Widget>[
  1065. // Outlined IconButton
  1066. IconToggleButton(
  1067. isEnabled: true,
  1068. tooltip: 'Outlined',
  1069. getDefaultStyle: enabledOutlinedButtonStyle,
  1070. ),
  1071. colDivider,
  1072. IconToggleButton(
  1073. isEnabled: false,
  1074. tooltip: 'Outlined (disabled)',
  1075. getDefaultStyle: disabledOutlinedButtonStyle,
  1076. ),
  1077. ],
  1078. ),
  1079. ],
  1080. ),
  1081. );
  1082. }
  1083. }
  1084. class IconToggleButton extends StatefulWidget {
  1085. const IconToggleButton({
  1086. required this.isEnabled,
  1087. required this.tooltip,
  1088. this.getDefaultStyle,
  1089. super.key,
  1090. });
  1091. final bool isEnabled;
  1092. final String tooltip;
  1093. final ButtonStyle? Function(bool, ColorScheme)? getDefaultStyle;
  1094. @override
  1095. State<IconToggleButton> createState() => _IconToggleButtonState();
  1096. }
  1097. class _IconToggleButtonState extends State<IconToggleButton> {
  1098. bool selected = false;
  1099. @override
  1100. Widget build(BuildContext context) {
  1101. final ColorScheme colors = Theme.of(context).colorScheme;
  1102. final VoidCallback? onPressed = widget.isEnabled
  1103. ? () {
  1104. setState(() {
  1105. selected = !selected;
  1106. });
  1107. }
  1108. : null;
  1109. ButtonStyle? style = widget.getDefaultStyle?.call(selected, colors);
  1110. return IconButton(
  1111. visualDensity: VisualDensity.standard,
  1112. isSelected: selected,
  1113. tooltip: widget.tooltip,
  1114. icon: const Icon(Icons.settings_outlined),
  1115. selectedIcon: const Icon(Icons.settings),
  1116. onPressed: onPressed,
  1117. style: style,
  1118. );
  1119. }
  1120. }
  1121. ButtonStyle enabledFilledButtonStyle(bool selected, ColorScheme colors) {
  1122. return IconButton.styleFrom(
  1123. foregroundColor: selected ? colors.onPrimary : colors.primary,
  1124. backgroundColor: selected ? colors.primary : colors.surfaceVariant,
  1125. disabledForegroundColor: colors.onSurface.withOpacity(0.38),
  1126. disabledBackgroundColor: colors.onSurface.withOpacity(0.12),
  1127. hoverColor: selected
  1128. ? colors.onPrimary.withOpacity(0.08)
  1129. : colors.primary.withOpacity(0.08),
  1130. focusColor: selected
  1131. ? colors.onPrimary.withOpacity(0.12)
  1132. : colors.primary.withOpacity(0.12),
  1133. highlightColor: selected
  1134. ? colors.onPrimary.withOpacity(0.12)
  1135. : colors.primary.withOpacity(0.12),
  1136. );
  1137. }
  1138. ButtonStyle disabledFilledButtonStyle(bool selected, ColorScheme colors) {
  1139. return IconButton.styleFrom(
  1140. disabledForegroundColor: colors.onSurface.withOpacity(0.38),
  1141. disabledBackgroundColor: colors.onSurface.withOpacity(0.12),
  1142. );
  1143. }
  1144. ButtonStyle enabledFilledTonalButtonStyle(bool selected, ColorScheme colors) {
  1145. return IconButton.styleFrom(
  1146. foregroundColor:
  1147. selected ? colors.onSecondaryContainer : colors.onSurfaceVariant,
  1148. backgroundColor:
  1149. selected ? colors.secondaryContainer : colors.surfaceVariant,
  1150. hoverColor: selected
  1151. ? colors.onSecondaryContainer.withOpacity(0.08)
  1152. : colors.onSurfaceVariant.withOpacity(0.08),
  1153. focusColor: selected
  1154. ? colors.onSecondaryContainer.withOpacity(0.12)
  1155. : colors.onSurfaceVariant.withOpacity(0.12),
  1156. highlightColor: selected
  1157. ? colors.onSecondaryContainer.withOpacity(0.12)
  1158. : colors.onSurfaceVariant.withOpacity(0.12),
  1159. );
  1160. }
  1161. ButtonStyle disabledFilledTonalButtonStyle(bool selected, ColorScheme colors) {
  1162. return IconButton.styleFrom(
  1163. disabledForegroundColor: colors.onSurface.withOpacity(0.38),
  1164. disabledBackgroundColor: colors.onSurface.withOpacity(0.12),
  1165. );
  1166. }
  1167. ButtonStyle enabledOutlinedButtonStyle(bool selected, ColorScheme colors) {
  1168. return IconButton.styleFrom(
  1169. backgroundColor: selected ? colors.inverseSurface : null,
  1170. hoverColor: selected
  1171. ? colors.onInverseSurface.withOpacity(0.08)
  1172. : colors.onSurfaceVariant.withOpacity(0.08),
  1173. focusColor: selected
  1174. ? colors.onInverseSurface.withOpacity(0.12)
  1175. : colors.onSurfaceVariant.withOpacity(0.12),
  1176. highlightColor: selected
  1177. ? colors.onInverseSurface.withOpacity(0.12)
  1178. : colors.onSurface.withOpacity(0.12),
  1179. side: BorderSide(color: colors.outline),
  1180. ).copyWith(
  1181. foregroundColor: MaterialStateProperty.resolveWith((states) {
  1182. if (states.contains(MaterialState.selected)) {
  1183. return colors.onInverseSurface;
  1184. }
  1185. if (states.contains(MaterialState.pressed)) {
  1186. return colors.onSurface;
  1187. }
  1188. return null;
  1189. }),
  1190. );
  1191. }
  1192. ButtonStyle disabledOutlinedButtonStyle(bool selected, ColorScheme colors) {
  1193. return IconButton.styleFrom(
  1194. disabledForegroundColor: colors.onSurface.withOpacity(0.38),
  1195. disabledBackgroundColor:
  1196. selected ? colors.onSurface.withOpacity(0.12) : null,
  1197. side: selected ? null : BorderSide(color: colors.outline.withOpacity(0.12)),
  1198. );
  1199. }
  1200. class Chips extends StatefulWidget {
  1201. const Chips({super.key});
  1202. @override
  1203. State<Chips> createState() => _ChipsState();
  1204. }
  1205. class _ChipsState extends State<Chips> {
  1206. bool isFiltered = true;
  1207. @override
  1208. Widget build(BuildContext context) {
  1209. return ComponentDecoration(
  1210. label: 'Chips',
  1211. tooltipMessage:
  1212. 'Use ActionChip, FilterChip, or InputChip. \nActionChip can also be used for suggestion chip',
  1213. child: Column(
  1214. crossAxisAlignment: CrossAxisAlignment.center,
  1215. children: <Widget>[
  1216. Wrap(
  1217. spacing: smallSpacing,
  1218. runSpacing: smallSpacing,
  1219. children: <Widget>[
  1220. ActionChip(
  1221. label: const Text('Assist'),
  1222. avatar: const Icon(Icons.event),
  1223. onPressed: () {},
  1224. ),
  1225. FilterChip(
  1226. label: const Text('Filter'),
  1227. selected: isFiltered,
  1228. onSelected: (selected) {
  1229. setState(() => isFiltered = selected);
  1230. },
  1231. ),
  1232. InputChip(
  1233. label: const Text('Input'),
  1234. onPressed: () {},
  1235. onDeleted: () {},
  1236. ),
  1237. ActionChip(
  1238. label: const Text('Suggestion'),
  1239. onPressed: () {},
  1240. ),
  1241. ],
  1242. ),
  1243. colDivider,
  1244. Wrap(
  1245. spacing: smallSpacing,
  1246. runSpacing: smallSpacing,
  1247. children: <Widget>[
  1248. const ActionChip(
  1249. label: Text('Assist'),
  1250. avatar: Icon(Icons.event),
  1251. ),
  1252. FilterChip(
  1253. label: const Text('Filter'),
  1254. selected: isFiltered,
  1255. onSelected: null,
  1256. ),
  1257. InputChip(
  1258. label: const Text('Input'),
  1259. onDeleted: () {},
  1260. isEnabled: false,
  1261. ),
  1262. const ActionChip(
  1263. label: Text('Suggestion'),
  1264. ),
  1265. ],
  1266. ),
  1267. ],
  1268. ),
  1269. );
  1270. }
  1271. }
  1272. class SegmentedButtons extends StatelessWidget {
  1273. const SegmentedButtons({super.key});
  1274. @override
  1275. Widget build(BuildContext context) {
  1276. return ComponentDecoration(
  1277. label: 'Segmented buttons',
  1278. tooltipMessage: 'Use SegmentedButton<T>',
  1279. child: Column(
  1280. children: const <Widget>[
  1281. SingleChoice(),
  1282. colDivider,
  1283. MultipleChoice(),
  1284. ],
  1285. ),
  1286. );
  1287. }
  1288. }
  1289. enum Calendar { day, week, month, year }
  1290. class SingleChoice extends StatefulWidget {
  1291. const SingleChoice({super.key});
  1292. @override
  1293. State<SingleChoice> createState() => _SingleChoiceState();
  1294. }
  1295. class _SingleChoiceState extends State<SingleChoice> {
  1296. Calendar calendarView = Calendar.day;
  1297. @override
  1298. Widget build(BuildContext context) {
  1299. return SegmentedButton<Calendar>(
  1300. segments: const <ButtonSegment<Calendar>>[
  1301. ButtonSegment<Calendar>(
  1302. value: Calendar.day,
  1303. label: Text('Day'),
  1304. icon: Icon(Icons.calendar_view_day)),
  1305. ButtonSegment<Calendar>(
  1306. value: Calendar.week,
  1307. label: Text('Week'),
  1308. icon: Icon(Icons.calendar_view_week)),
  1309. ButtonSegment<Calendar>(
  1310. value: Calendar.month,
  1311. label: Text('Month'),
  1312. icon: Icon(Icons.calendar_view_month)),
  1313. ButtonSegment<Calendar>(
  1314. value: Calendar.year,
  1315. label: Text('Year'),
  1316. icon: Icon(Icons.calendar_today)),
  1317. ],
  1318. selected: <Calendar>{calendarView},
  1319. onSelectionChanged: (newSelection) {
  1320. setState(() {
  1321. // By default there is only a single segment that can be
  1322. // selected at one time, so its value is always the first
  1323. // item in the selected set.
  1324. calendarView = newSelection.first;
  1325. });
  1326. },
  1327. );
  1328. }
  1329. }
  1330. enum Sizes { extraSmall, small, medium, large, extraLarge }
  1331. class MultipleChoice extends StatefulWidget {
  1332. const MultipleChoice({super.key});
  1333. @override
  1334. State<MultipleChoice> createState() => _MultipleChoiceState();
  1335. }
  1336. class _MultipleChoiceState extends State<MultipleChoice> {
  1337. Set<Sizes> selection = <Sizes>{Sizes.large, Sizes.extraLarge};
  1338. @override
  1339. Widget build(BuildContext context) {
  1340. return SegmentedButton<Sizes>(
  1341. segments: const <ButtonSegment<Sizes>>[
  1342. ButtonSegment<Sizes>(value: Sizes.extraSmall, label: Text('XS')),
  1343. ButtonSegment<Sizes>(value: Sizes.small, label: Text('S')),
  1344. ButtonSegment<Sizes>(value: Sizes.medium, label: Text('M')),
  1345. ButtonSegment<Sizes>(
  1346. value: Sizes.large,
  1347. label: Text('L'),
  1348. ),
  1349. ButtonSegment<Sizes>(value: Sizes.extraLarge, label: Text('XL')),
  1350. ],
  1351. selected: selection,
  1352. onSelectionChanged: (newSelection) {
  1353. setState(() {
  1354. selection = newSelection;
  1355. });
  1356. },
  1357. multiSelectionEnabled: true,
  1358. );
  1359. }
  1360. }
  1361. class SnackBarSection extends StatelessWidget {
  1362. const SnackBarSection({super.key});
  1363. @override
  1364. Widget build(BuildContext context) {
  1365. return ComponentDecoration(
  1366. label: 'Snackbar',
  1367. tooltipMessage:
  1368. 'Use ScaffoldMessenger.of(context).showSnackBar with SnackBar',
  1369. child: TextButton(
  1370. onPressed: () {
  1371. final snackBar = SnackBar(
  1372. behavior: SnackBarBehavior.floating,
  1373. width: 400.0,
  1374. content: const Text('This is a snackbar'),
  1375. action: SnackBarAction(
  1376. label: 'Close',
  1377. onPressed: () {},
  1378. ),
  1379. );
  1380. ScaffoldMessenger.of(context).hideCurrentSnackBar();
  1381. ScaffoldMessenger.of(context).showSnackBar(snackBar);
  1382. },
  1383. child: const Text(
  1384. 'Show snackbar',
  1385. style: TextStyle(fontWeight: FontWeight.bold),
  1386. ),
  1387. ),
  1388. );
  1389. }
  1390. }
  1391. class BottomSheetSection extends StatefulWidget {
  1392. const BottomSheetSection({super.key});
  1393. @override
  1394. State<BottomSheetSection> createState() => _BottomSheetSectionState();
  1395. }
  1396. class _BottomSheetSectionState extends State<BottomSheetSection> {
  1397. bool isNonModalBottomSheetOpen = false;
  1398. PersistentBottomSheetController<void>? _nonModalBottomSheetController;
  1399. @override
  1400. Widget build(BuildContext context) {
  1401. List<Widget> buttonList = <Widget>[
  1402. IconButton(onPressed: () {}, icon: const Icon(Icons.share_outlined)),
  1403. IconButton(onPressed: () {}, icon: const Icon(Icons.add)),
  1404. IconButton(onPressed: () {}, icon: const Icon(Icons.delete_outline)),
  1405. IconButton(onPressed: () {}, icon: const Icon(Icons.archive_outlined)),
  1406. IconButton(onPressed: () {}, icon: const Icon(Icons.settings_outlined)),
  1407. IconButton(onPressed: () {}, icon: const Icon(Icons.favorite_border)),
  1408. ];
  1409. List<Text> labelList = const <Text>[
  1410. Text('Share'),
  1411. Text('Add to'),
  1412. Text('Trash'),
  1413. Text('Archive'),
  1414. Text('Settings'),
  1415. Text('Favorite')
  1416. ];
  1417. buttonList = List.generate(
  1418. buttonList.length,
  1419. (index) => Padding(
  1420. padding: const EdgeInsets.fromLTRB(20.0, 30.0, 20.0, 20.0),
  1421. child: Column(
  1422. mainAxisAlignment: MainAxisAlignment.start,
  1423. children: [
  1424. buttonList[index],
  1425. labelList[index],
  1426. ],
  1427. ),
  1428. ));
  1429. return ComponentDecoration(
  1430. label: 'Bottom sheet',
  1431. tooltipMessage: 'Use showModalBottomSheet<T> or showBottomSheet<T>',
  1432. child: Wrap(
  1433. alignment: WrapAlignment.spaceEvenly,
  1434. children: [
  1435. TextButton(
  1436. child: const Text(
  1437. 'Show modal bottom sheet',
  1438. style: TextStyle(fontWeight: FontWeight.bold),
  1439. ),
  1440. onPressed: () {
  1441. showModalBottomSheet<void>(
  1442. context: context,
  1443. // TODO: Remove when this is in the framework https://github.com/flutter/flutter/issues/118619
  1444. constraints: const BoxConstraints(maxWidth: 640),
  1445. builder: (context) {
  1446. return SizedBox(
  1447. height: 150,
  1448. child: Padding(
  1449. padding: const EdgeInsets.symmetric(horizontal: 32.0),
  1450. child: ListView(
  1451. shrinkWrap: true,
  1452. scrollDirection: Axis.horizontal,
  1453. children: buttonList,
  1454. ),
  1455. ),
  1456. );
  1457. },
  1458. );
  1459. },
  1460. ),
  1461. TextButton(
  1462. child: Text(
  1463. isNonModalBottomSheetOpen
  1464. ? 'Hide bottom sheet'
  1465. : 'Show bottom sheet',
  1466. style: const TextStyle(fontWeight: FontWeight.bold),
  1467. ),
  1468. onPressed: () {
  1469. if (isNonModalBottomSheetOpen) {
  1470. _nonModalBottomSheetController?.close();
  1471. setState(() {
  1472. isNonModalBottomSheetOpen = false;
  1473. });
  1474. return;
  1475. } else {
  1476. setState(() {
  1477. isNonModalBottomSheetOpen = true;
  1478. });
  1479. }
  1480. _nonModalBottomSheetController = showBottomSheet<void>(
  1481. elevation: 8.0,
  1482. context: context,
  1483. // TODO: Remove when this is in the framework https://github.com/flutter/flutter/issues/118619
  1484. constraints: const BoxConstraints(maxWidth: 640),
  1485. builder: (context) {
  1486. return SizedBox(
  1487. height: 150,
  1488. child: Padding(
  1489. padding: const EdgeInsets.symmetric(horizontal: 32.0),
  1490. child: ListView(
  1491. shrinkWrap: true,
  1492. scrollDirection: Axis.horizontal,
  1493. children: buttonList,
  1494. ),
  1495. ),
  1496. );
  1497. },
  1498. );
  1499. },
  1500. ),
  1501. ],
  1502. ),
  1503. );
  1504. }
  1505. }
  1506. class BottomAppBars extends StatelessWidget {
  1507. const BottomAppBars({super.key});
  1508. @override
  1509. Widget build(BuildContext context) {
  1510. return ComponentDecoration(
  1511. label: 'Bottom app bar',
  1512. tooltipMessage: 'Use BottomAppBar',
  1513. child: Column(
  1514. children: [
  1515. SizedBox(
  1516. height: 80,
  1517. child: Scaffold(
  1518. floatingActionButton: FloatingActionButton(
  1519. onPressed: () {},
  1520. elevation: 0.0,
  1521. child: const Icon(Icons.add),
  1522. ),
  1523. floatingActionButtonLocation:
  1524. FloatingActionButtonLocation.endContained,
  1525. bottomNavigationBar: BottomAppBar(
  1526. child: Row(
  1527. children: <Widget>[
  1528. const IconButtonAnchorExample(),
  1529. IconButton(
  1530. tooltip: 'Search',
  1531. icon: const Icon(Icons.search),
  1532. onPressed: () {},
  1533. ),
  1534. IconButton(
  1535. tooltip: 'Favorite',
  1536. icon: const Icon(Icons.favorite),
  1537. onPressed: () {},
  1538. ),
  1539. ],
  1540. ),
  1541. ),
  1542. ),
  1543. ),
  1544. ],
  1545. ),
  1546. );
  1547. }
  1548. }
  1549. class IconButtonAnchorExample extends StatelessWidget {
  1550. const IconButtonAnchorExample({super.key});
  1551. @override
  1552. Widget build(BuildContext context) {
  1553. return MenuAnchor(
  1554. builder: (context, controller, child) {
  1555. return IconButton(
  1556. onPressed: () {
  1557. if (controller.isOpen) {
  1558. controller.close();
  1559. } else {
  1560. controller.open();
  1561. }
  1562. },
  1563. icon: const Icon(Icons.more_vert),
  1564. );
  1565. },
  1566. menuChildren: [
  1567. MenuItemButton(
  1568. child: const Text('Menu 1'),
  1569. onPressed: () {},
  1570. ),
  1571. MenuItemButton(
  1572. child: const Text('Menu 2'),
  1573. onPressed: () {},
  1574. ),
  1575. SubmenuButton(
  1576. menuChildren: <Widget>[
  1577. MenuItemButton(
  1578. onPressed: () {},
  1579. child: const Text('Menu 3.1'),
  1580. ),
  1581. MenuItemButton(
  1582. onPressed: () {},
  1583. child: const Text('Menu 3.2'),
  1584. ),
  1585. MenuItemButton(
  1586. onPressed: () {},
  1587. child: const Text('Menu 3.3'),
  1588. ),
  1589. ],
  1590. child: const Text('Menu 3'),
  1591. ),
  1592. ],
  1593. );
  1594. }
  1595. }
  1596. class ButtonAnchorExample extends StatelessWidget {
  1597. const ButtonAnchorExample({super.key});
  1598. @override
  1599. Widget build(BuildContext context) {
  1600. return MenuAnchor(
  1601. builder: (context, controller, child) {
  1602. return FilledButton.tonal(
  1603. onPressed: () {
  1604. if (controller.isOpen) {
  1605. controller.close();
  1606. } else {
  1607. controller.open();
  1608. }
  1609. },
  1610. child: const Text('Show menu'),
  1611. );
  1612. },
  1613. menuChildren: [
  1614. MenuItemButton(
  1615. leadingIcon: const Icon(Icons.people_alt_outlined),
  1616. child: const Text('Item 1'),
  1617. onPressed: () {},
  1618. ),
  1619. MenuItemButton(
  1620. leadingIcon: const Icon(Icons.remove_red_eye_outlined),
  1621. child: const Text('Item 2'),
  1622. onPressed: () {},
  1623. ),
  1624. MenuItemButton(
  1625. leadingIcon: const Icon(Icons.refresh),
  1626. onPressed: () {},
  1627. child: const Text('Item 3'),
  1628. ),
  1629. ],
  1630. );
  1631. }
  1632. }
  1633. class NavigationDrawers extends StatelessWidget {
  1634. const NavigationDrawers({super.key, required this.scaffoldKey});
  1635. final GlobalKey<ScaffoldState> scaffoldKey;
  1636. @override
  1637. Widget build(BuildContext context) {
  1638. return ComponentDecoration(
  1639. label: 'Navigation drawer',
  1640. tooltipMessage:
  1641. 'Use NavigationDrawer. For modal navigation drawers, see Scaffold.endDrawer',
  1642. child: Column(
  1643. children: [
  1644. const SizedBox(height: 520, child: NavigationDrawerSection()),
  1645. colDivider,
  1646. colDivider,
  1647. TextButton(
  1648. child: const Text('Show modal navigation drawer',
  1649. style: TextStyle(fontWeight: FontWeight.bold)),
  1650. onPressed: () {
  1651. scaffoldKey.currentState!.openEndDrawer();
  1652. },
  1653. ),
  1654. ],
  1655. ),
  1656. );
  1657. }
  1658. }
  1659. class NavigationDrawerSection extends StatefulWidget {
  1660. const NavigationDrawerSection({super.key});
  1661. @override
  1662. State<NavigationDrawerSection> createState() =>
  1663. _NavigationDrawerSectionState();
  1664. }
  1665. class _NavigationDrawerSectionState extends State<NavigationDrawerSection> {
  1666. int navDrawerIndex = 0;
  1667. @override
  1668. Widget build(BuildContext context) {
  1669. return NavigationDrawer(
  1670. onDestinationSelected: (selectedIndex) {
  1671. setState(() {
  1672. navDrawerIndex = selectedIndex;
  1673. });
  1674. },
  1675. selectedIndex: navDrawerIndex,
  1676. children: <Widget>[
  1677. Padding(
  1678. padding: const EdgeInsets.fromLTRB(28, 16, 16, 10),
  1679. child: Text(
  1680. 'Mail',
  1681. style: Theme.of(context).textTheme.titleSmall,
  1682. ),
  1683. ),
  1684. ...destinations.map((destination) {
  1685. return NavigationDrawerDestination(
  1686. label: Text(destination.label),
  1687. icon: destination.icon,
  1688. selectedIcon: destination.selectedIcon,
  1689. );
  1690. }),
  1691. const Divider(indent: 28, endIndent: 28),
  1692. Padding(
  1693. padding: const EdgeInsets.fromLTRB(28, 16, 16, 10),
  1694. child: Text(
  1695. 'Labels',
  1696. style: Theme.of(context).textTheme.titleSmall,
  1697. ),
  1698. ),
  1699. ...labelDestinations.map((destination) {
  1700. return NavigationDrawerDestination(
  1701. label: Text(destination.label),
  1702. icon: destination.icon,
  1703. selectedIcon: destination.selectedIcon,
  1704. );
  1705. }),
  1706. ],
  1707. );
  1708. }
  1709. }
  1710. class ExampleDestination {
  1711. const ExampleDestination(this.label, this.icon, this.selectedIcon);
  1712. final String label;
  1713. final Widget icon;
  1714. final Widget selectedIcon;
  1715. }
  1716. const List<ExampleDestination> destinations = <ExampleDestination>[
  1717. ExampleDestination('Inbox', Icon(Icons.inbox_outlined), Icon(Icons.inbox)),
  1718. ExampleDestination('Outbox', Icon(Icons.send_outlined), Icon(Icons.send)),
  1719. ExampleDestination(
  1720. 'Favorites', Icon(Icons.favorite_outline), Icon(Icons.favorite)),
  1721. ExampleDestination('Trash', Icon(Icons.delete_outline), Icon(Icons.delete)),
  1722. ];
  1723. const List<ExampleDestination> labelDestinations = <ExampleDestination>[
  1724. ExampleDestination(
  1725. 'Family', Icon(Icons.bookmark_border), Icon(Icons.bookmark)),
  1726. ExampleDestination(
  1727. 'School', Icon(Icons.bookmark_border), Icon(Icons.bookmark)),
  1728. ExampleDestination('Work', Icon(Icons.bookmark_border), Icon(Icons.bookmark)),
  1729. ];
  1730. class NavigationRails extends StatelessWidget {
  1731. const NavigationRails({super.key});
  1732. @override
  1733. Widget build(BuildContext context) {
  1734. return const ComponentDecoration(
  1735. label: 'Navigation rail',
  1736. tooltipMessage: 'Use NavigationRail',
  1737. child: IntrinsicWidth(
  1738. child: SizedBox(height: 420, child: NavigationRailSection())),
  1739. );
  1740. }
  1741. }
  1742. class NavigationRailSection extends StatefulWidget {
  1743. const NavigationRailSection({super.key});
  1744. @override
  1745. State<NavigationRailSection> createState() => _NavigationRailSectionState();
  1746. }
  1747. class _NavigationRailSectionState extends State<NavigationRailSection> {
  1748. int navRailIndex = 0;
  1749. @override
  1750. Widget build(BuildContext context) {
  1751. return NavigationRail(
  1752. onDestinationSelected: (selectedIndex) {
  1753. setState(() {
  1754. navRailIndex = selectedIndex;
  1755. });
  1756. },
  1757. elevation: 4,
  1758. leading: FloatingActionButton(
  1759. child: const Icon(Icons.create), onPressed: () {}),
  1760. groupAlignment: 0.0,
  1761. selectedIndex: navRailIndex,
  1762. labelType: NavigationRailLabelType.selected,
  1763. destinations: <NavigationRailDestination>[
  1764. ...destinations.map((destination) {
  1765. return NavigationRailDestination(
  1766. label: Text(destination.label),
  1767. icon: destination.icon,
  1768. selectedIcon: destination.selectedIcon,
  1769. );
  1770. }),
  1771. ],
  1772. );
  1773. }
  1774. }
  1775. class Tabs extends StatefulWidget {
  1776. const Tabs({super.key});
  1777. @override
  1778. State<Tabs> createState() => _TabsState();
  1779. }
  1780. class _TabsState extends State<Tabs> with TickerProviderStateMixin {
  1781. late TabController _tabController;
  1782. @override
  1783. void initState() {
  1784. super.initState();
  1785. _tabController = TabController(length: 3, vsync: this);
  1786. }
  1787. @override
  1788. Widget build(BuildContext context) {
  1789. return ComponentDecoration(
  1790. label: 'Tabs',
  1791. tooltipMessage: 'Use TabBar',
  1792. child: SizedBox(
  1793. height: 80,
  1794. child: Scaffold(
  1795. appBar: AppBar(
  1796. bottom: TabBar(
  1797. controller: _tabController,
  1798. tabs: const <Widget>[
  1799. Tab(
  1800. icon: Icon(Icons.videocam_outlined),
  1801. text: 'Video',
  1802. iconMargin: EdgeInsets.only(bottom: 0.0),
  1803. ),
  1804. Tab(
  1805. icon: Icon(Icons.photo_outlined),
  1806. text: 'Photos',
  1807. iconMargin: EdgeInsets.only(bottom: 0.0),
  1808. ),
  1809. Tab(
  1810. icon: Icon(Icons.audiotrack_sharp),
  1811. text: 'Audio',
  1812. iconMargin: EdgeInsets.only(bottom: 0.0),
  1813. ),
  1814. ],
  1815. ),
  1816. // TODO: Showcase secondary tab bar https://github.com/flutter/flutter/issues/111962
  1817. ),
  1818. ),
  1819. ),
  1820. );
  1821. }
  1822. }
  1823. class TopAppBars extends StatelessWidget {
  1824. const TopAppBars({super.key});
  1825. static final actions = [
  1826. IconButton(icon: const Icon(Icons.attach_file), onPressed: () {}),
  1827. IconButton(icon: const Icon(Icons.event), onPressed: () {}),
  1828. IconButton(icon: const Icon(Icons.more_vert), onPressed: () {}),
  1829. ];
  1830. @override
  1831. Widget build(BuildContext context) {
  1832. return ComponentDecoration(
  1833. label: 'Top app bars',
  1834. tooltipMessage:
  1835. 'Use AppBar, SliverAppBar, SliverAppBar.medium, or SliverAppBar.large',
  1836. child: Column(
  1837. children: [
  1838. AppBar(
  1839. title: const Text('Center-aligned'),
  1840. leading: const BackButton(),
  1841. actions: [
  1842. IconButton(
  1843. iconSize: 32,
  1844. icon: const Icon(Icons.account_circle_outlined),
  1845. onPressed: () {},
  1846. ),
  1847. ],
  1848. centerTitle: true,
  1849. ),
  1850. colDivider,
  1851. AppBar(
  1852. title: const Text('Small'),
  1853. leading: const BackButton(),
  1854. actions: actions,
  1855. centerTitle: false,
  1856. ),
  1857. colDivider,
  1858. SizedBox(
  1859. height: 100,
  1860. child: CustomScrollView(
  1861. slivers: [
  1862. SliverAppBar.medium(
  1863. title: const Text('Medium'),
  1864. leading: const BackButton(),
  1865. actions: actions,
  1866. ),
  1867. const SliverFillRemaining(),
  1868. ],
  1869. ),
  1870. ),
  1871. colDivider,
  1872. SizedBox(
  1873. height: 130,
  1874. child: CustomScrollView(
  1875. slivers: [
  1876. SliverAppBar.large(
  1877. title: const Text('Large'),
  1878. leading: const BackButton(),
  1879. actions: actions,
  1880. ),
  1881. const SliverFillRemaining(),
  1882. ],
  1883. ),
  1884. ),
  1885. ],
  1886. ),
  1887. );
  1888. }
  1889. }
  1890. class Menus extends StatefulWidget {
  1891. const Menus({super.key});
  1892. @override
  1893. State<Menus> createState() => _MenusState();
  1894. }
  1895. class _MenusState extends State<Menus> {
  1896. final TextEditingController colorController = TextEditingController();
  1897. final TextEditingController iconController = TextEditingController();
  1898. IconLabel? selectedIcon = IconLabel.smile;
  1899. ColorLabel? selectedColor;
  1900. @override
  1901. Widget build(BuildContext context) {
  1902. final List<DropdownMenuEntry<ColorLabel>> colorEntries =
  1903. <DropdownMenuEntry<ColorLabel>>[];
  1904. for (final ColorLabel color in ColorLabel.values) {
  1905. colorEntries.add(DropdownMenuEntry<ColorLabel>(
  1906. value: color, label: color.label, enabled: color.label != 'Grey'));
  1907. }
  1908. final List<DropdownMenuEntry<IconLabel>> iconEntries =
  1909. <DropdownMenuEntry<IconLabel>>[];
  1910. for (final IconLabel icon in IconLabel.values) {
  1911. iconEntries
  1912. .add(DropdownMenuEntry<IconLabel>(value: icon, label: icon.label));
  1913. }
  1914. return ComponentDecoration(
  1915. label: 'Menus',
  1916. tooltipMessage: 'Use MenuAnchor or DropdownMenu<T>',
  1917. child: Column(
  1918. children: [
  1919. Row(
  1920. mainAxisAlignment: MainAxisAlignment.center,
  1921. children: const <Widget>[
  1922. ButtonAnchorExample(),
  1923. rowDivider,
  1924. IconButtonAnchorExample(),
  1925. ],
  1926. ),
  1927. colDivider,
  1928. Wrap(
  1929. alignment: WrapAlignment.spaceAround,
  1930. runAlignment: WrapAlignment.center,
  1931. crossAxisAlignment: WrapCrossAlignment.center,
  1932. spacing: smallSpacing,
  1933. runSpacing: smallSpacing,
  1934. children: [
  1935. DropdownMenu<ColorLabel>(
  1936. controller: colorController,
  1937. label: const Text('Color'),
  1938. enableFilter: true,
  1939. dropdownMenuEntries: colorEntries,
  1940. inputDecorationTheme: const InputDecorationTheme(filled: true),
  1941. onSelected: (color) {
  1942. setState(() {
  1943. selectedColor = color;
  1944. });
  1945. },
  1946. ),
  1947. DropdownMenu<IconLabel>(
  1948. initialSelection: IconLabel.smile,
  1949. controller: iconController,
  1950. leadingIcon: const Icon(Icons.search),
  1951. label: const Text('Icon'),
  1952. dropdownMenuEntries: iconEntries,
  1953. onSelected: (icon) {
  1954. setState(() {
  1955. selectedIcon = icon;
  1956. });
  1957. },
  1958. ),
  1959. Icon(
  1960. selectedIcon?.icon,
  1961. color: selectedColor?.color ?? Colors.grey.withOpacity(0.5),
  1962. )
  1963. ],
  1964. ),
  1965. ],
  1966. ),
  1967. );
  1968. }
  1969. }
  1970. enum ColorLabel {
  1971. blue('Blue', Colors.blue),
  1972. pink('Pink', Colors.pink),
  1973. green('Green', Colors.green),
  1974. yellow('Yellow', Colors.yellow),
  1975. grey('Grey', Colors.grey);
  1976. const ColorLabel(this.label, this.color);
  1977. final String label;
  1978. final Color color;
  1979. }
  1980. enum IconLabel {
  1981. smile('Smile', Icons.sentiment_satisfied_outlined),
  1982. cloud(
  1983. 'Cloud',
  1984. Icons.cloud_outlined,
  1985. ),
  1986. brush('Brush', Icons.brush_outlined),
  1987. heart('Heart', Icons.favorite);
  1988. const IconLabel(this.label, this.icon);
  1989. final String label;
  1990. final IconData icon;
  1991. }
  1992. class Sliders extends StatefulWidget {
  1993. const Sliders({super.key});
  1994. @override
  1995. State<Sliders> createState() => _SlidersState();
  1996. }
  1997. class _SlidersState extends State<Sliders> {
  1998. double sliderValue0 = 30.0;
  1999. double sliderValue1 = 20.0;
  2000. @override
  2001. Widget build(BuildContext context) {
  2002. return ComponentDecoration(
  2003. label: 'Sliders',
  2004. tooltipMessage: 'Use Slider or RangeSlider',
  2005. child: Column(
  2006. children: <Widget>[
  2007. Slider(
  2008. max: 100,
  2009. value: sliderValue0,
  2010. onChanged: (value) {
  2011. setState(() {
  2012. sliderValue0 = value;
  2013. });
  2014. },
  2015. ),
  2016. const SizedBox(height: 20),
  2017. Slider(
  2018. max: 100,
  2019. divisions: 5,
  2020. value: sliderValue1,
  2021. label: sliderValue1.round().toString(),
  2022. onChanged: (value) {
  2023. setState(() {
  2024. sliderValue1 = value;
  2025. });
  2026. },
  2027. ),
  2028. ],
  2029. ));
  2030. }
  2031. }
  2032. class ComponentDecoration extends StatefulWidget {
  2033. const ComponentDecoration({
  2034. super.key,
  2035. required this.label,
  2036. required this.child,
  2037. this.tooltipMessage = '',
  2038. });
  2039. final String label;
  2040. final Widget child;
  2041. final String? tooltipMessage;
  2042. @override
  2043. State<ComponentDecoration> createState() => _ComponentDecorationState();
  2044. }
  2045. class _ComponentDecorationState extends State<ComponentDecoration> {
  2046. final focusNode = FocusNode();
  2047. @override
  2048. Widget build(BuildContext context) {
  2049. return RepaintBoundary(
  2050. child: Padding(
  2051. padding: const EdgeInsets.symmetric(vertical: smallSpacing),
  2052. child: Column(
  2053. children: [
  2054. Row(
  2055. mainAxisAlignment: MainAxisAlignment.center,
  2056. children: [
  2057. Text(widget.label,
  2058. style: Theme.of(context).textTheme.titleSmall),
  2059. Tooltip(
  2060. message: widget.tooltipMessage,
  2061. child: const Padding(
  2062. padding: EdgeInsets.symmetric(horizontal: 5.0),
  2063. child: Icon(Icons.info_outline, size: 16)),
  2064. ),
  2065. ],
  2066. ),
  2067. ConstrainedBox(
  2068. constraints:
  2069. const BoxConstraints.tightFor(width: widthConstraint),
  2070. // Tapping within the a component card should request focus
  2071. // for that component's children.
  2072. child: Focus(
  2073. focusNode: focusNode,
  2074. canRequestFocus: true,
  2075. child: GestureDetector(
  2076. onTapDown: (_) {
  2077. focusNode.requestFocus();
  2078. },
  2079. behavior: HitTestBehavior.opaque,
  2080. child: Card(
  2081. elevation: 0,
  2082. shape: RoundedRectangleBorder(
  2083. side: BorderSide(
  2084. color: Theme.of(context).colorScheme.outlineVariant,
  2085. ),
  2086. borderRadius: const BorderRadius.all(Radius.circular(12)),
  2087. ),
  2088. child: Padding(
  2089. padding: const EdgeInsets.symmetric(
  2090. horizontal: 5.0, vertical: 20.0),
  2091. child: Center(
  2092. child: widget.child,
  2093. ),
  2094. ),
  2095. ),
  2096. ),
  2097. ),
  2098. ),
  2099. ],
  2100. ),
  2101. ),
  2102. );
  2103. }
  2104. }
  2105. class ComponentGroupDecoration extends StatelessWidget {
  2106. const ComponentGroupDecoration(
  2107. {super.key, required this.label, required this.children});
  2108. final String label;
  2109. final List<Widget> children;
  2110. @override
  2111. Widget build(BuildContext context) {
  2112. // Fully traverse this component group before moving on
  2113. return FocusTraversalGroup(
  2114. child: Card(
  2115. margin: EdgeInsets.zero,
  2116. elevation: 0,
  2117. color: Theme.of(context).colorScheme.surfaceVariant.withOpacity(0.3),
  2118. child: Padding(
  2119. padding: const EdgeInsets.symmetric(vertical: 20.0),
  2120. child: Center(
  2121. child: Column(
  2122. children: [
  2123. Text(label, style: Theme.of(context).textTheme.titleLarge),
  2124. colDivider,
  2125. ...children
  2126. ],
  2127. ),
  2128. ),
  2129. ),
  2130. ),
  2131. );
  2132. }
  2133. }