Last active
May 20, 2020 21:12
-
-
Save stegrams/02dc9d480ed2e01e81341d34b23800b4 to your computer and use it in GitHub Desktop.
X-Wei/flutter_catalog basic demos GUI for ListView study.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'package:flutter/material.dart'; | |
void main() => runApp(MyApp()); | |
class MyApp extends StatelessWidget { | |
@override | |
Widget build(BuildContext context) { | |
return MaterialApp( | |
title: 'Flutter Catalog', | |
home: MyHomePage(title: 'Flutter Catalog'), | |
); | |
} | |
} | |
class MyHomePage extends StatefulWidget { | |
MyHomePage({Key key, this.title}) : super(key: key); | |
final String title; | |
@override | |
_MyHomePageState createState() => _MyHomePageState(); | |
} | |
class _MyHomePageState extends State<MyHomePage> { | |
List<MyRouteGroup> groupRoutes = kMyAppRoutesBasic; | |
@override | |
Widget build(BuildContext context) { | |
return Scaffold( | |
appBar: AppBar( | |
title: Text(widget.title), | |
), | |
body: ListView( | |
children: groupRoutes | |
.map<Widget>( | |
(group) => MyRouteGroupWidget( | |
myRouteGroup: group, | |
onUpdate: (newGroup) { | |
setState(() { | |
final groups = groupRoutes.toList(); | |
groups[groups.indexOf(group)] = newGroup; | |
groupRoutes = groups; | |
}); | |
}, | |
), | |
) | |
.toList(), | |
), | |
); | |
} | |
} | |
class MyRoute { | |
const MyRoute({@required this.title, this.description}); | |
final String title; | |
final String description; | |
MyRoute copyWith({String title, String description}) => MyRoute( | |
title: title ?? this.title, | |
description: description ?? this.description, | |
); | |
} | |
class MyRouteWidget extends StatefulWidget { | |
MyRouteWidget({ | |
Key key, | |
this.myRoute, | |
this.onUpdate, | |
}) : super(key: key); | |
final MyRoute myRoute; | |
final ValueSetter<MyRoute> onUpdate; | |
@override | |
_MyRouteWidgetState createState() => _MyRouteWidgetState(); | |
} | |
class _MyRouteWidgetState extends State<MyRouteWidget> { | |
@override | |
void initState() { | |
super.initState(); | |
edit = false; | |
myRoute = widget.myRoute; | |
controller = TextEditingController(text: myRoute.title); | |
focusNode = FocusNode(); | |
focusNode.addListener(() { | |
if (!focusNode.hasPrimaryFocus) onLostFocus(); | |
}); | |
} | |
@override | |
void dispose() { | |
focusNode.dispose(); | |
controller.dispose(); | |
super.dispose(); | |
} | |
void onLostFocus() { | |
setState(() { | |
controller.text = myRoute.title; | |
edit = false; | |
}); | |
if (widget.myRoute != myRoute) widget.onUpdate(myRoute); | |
} | |
bool edit; | |
MyRoute myRoute; | |
TextEditingController controller; | |
FocusNode focusNode; | |
@override | |
Widget build(BuildContext context) { | |
final routeTitleTextStyle = | |
ThemeData().textTheme.bodyText2.copyWith(fontWeight: FontWeight.bold); | |
return ListTile( | |
leading: IconButton( | |
tooltip: 'Bookmark', | |
icon: Icon(Icons.star_border, color: Colors.grey), | |
onPressed: null, | |
), | |
title: edit | |
? TextFormField( | |
focusNode: focusNode, | |
controller: controller, | |
onFieldSubmitted: (title) { | |
if (title == myRoute.title) return; | |
setState(() => myRoute = myRoute.copyWith(title: title)); | |
}, | |
) | |
: Text( | |
myRoute.title, | |
style: routeTitleTextStyle, | |
), | |
trailing: edit | |
? null | |
: IconButton( | |
icon: Icon(Icons.edit), | |
onPressed: () { | |
setState(() => edit = true); | |
focusNode.requestFocus(); | |
// On focus sellect all | |
controller.selection = TextSelection( | |
baseOffset: 0, | |
extentOffset: controller.text.length, | |
); | |
}, | |
), | |
subtitle: myRoute.description == null ? null : Text(myRoute.description), | |
onTap: null, | |
); | |
} | |
} | |
class MyRouteGroup { | |
const MyRouteGroup( | |
{@required this.groupName, @required this.icon, @required this.routes}); | |
final String groupName; | |
final Widget icon; | |
final List<MyRoute> routes; | |
MyRouteGroup copyWith( | |
{String groupName, Widget icon, List<MyRoute> routes}) => | |
MyRouteGroup( | |
groupName: groupName ?? this.groupName, | |
icon: icon ?? this.icon, | |
routes: routes ?? this.routes, | |
); | |
} | |
class MyRouteGroupWidget extends StatelessWidget { | |
const MyRouteGroupWidget({ | |
Key key, | |
this.myRouteGroup, | |
this.onUpdate, | |
}) : super(key: key); | |
final MyRouteGroup myRouteGroup; | |
final ValueSetter<MyRouteGroup> onUpdate; | |
@override | |
Widget build(BuildContext context) { | |
return Card( | |
child: ExpansionTile( | |
leading: myRouteGroup.icon, | |
title: Text( | |
myRouteGroup.groupName, | |
style: Theme.of(context).textTheme.headline6, | |
), | |
children: myRouteGroup.routes | |
.map( | |
(route) => MyRouteWidget( | |
myRoute: route, | |
onUpdate: (newRoute) { | |
final routes = myRouteGroup.routes.toList(); | |
routes[routes.indexOf(route)] = newRoute; | |
onUpdate(myRouteGroup.copyWith(routes: routes)); | |
}, | |
), | |
) | |
.toList(), | |
), | |
); | |
} | |
} | |
var kMyAppRoutesBasic = <MyRouteGroup>[ | |
MyRouteGroup( | |
groupName: 'Widgets', | |
icon: Icon(Icons.extension), | |
routes: <MyRoute>[ | |
MyRoute( | |
title: 'Icon', | |
), | |
MyRoute( | |
title: 'Text', | |
), | |
MyRoute( | |
title: 'TextField', | |
description: 'Text input.', | |
), | |
MyRoute( | |
title: 'TextFormField', | |
description: 'Convenience widget wrapping a TextField in a FormField.', | |
), | |
MyRoute( | |
title: 'Image', | |
), | |
MyRoute( | |
title: 'Card, Inkwell', | |
description: | |
'Container with corner and shadow; inkwell (ripple) effects', | |
), | |
MyRoute( | |
title: 'Buttons', | |
description: | |
'RaisedButton, FlatButton, OutlineButton, IconButton&Tooltips', | |
), | |
MyRoute( | |
title: 'DropdownButton, MenuButton', | |
), | |
MyRoute( | |
title: 'Other stateful widgets', | |
description: 'Switch, CheckBox, Slider, etc.', | |
), | |
], | |
), | |
MyRouteGroup( | |
groupName: 'Layouts', | |
icon: Icon(Icons.dashboard), | |
routes: <MyRoute>[ | |
MyRoute( | |
title: 'Container', | |
description: 'Basic widgets for layout.', | |
), | |
MyRoute( | |
title: 'Row, Column', | |
description: 'Align chidren widgets linearly.', | |
), | |
MyRoute( | |
title: 'Wrap', | |
description: 'Wrap to the next row/column when run out of room.', | |
), | |
MyRoute( | |
title: 'Expanded, SizedBox', | |
description: 'Dividing space by "weights" (flex).', | |
), | |
MyRoute( | |
title: 'Stack', | |
description: 'Putting widget on top of another.', | |
), | |
], | |
), | |
MyRouteGroup( | |
groupName: 'Lists', | |
icon: Icon(Icons.format_list_numbered), | |
routes: <MyRoute>[ | |
MyRoute( | |
title: 'ListTile', | |
), | |
MyRoute( | |
title: 'ListView.builder', | |
description: 'Building list items with a builder.', | |
), | |
MyRoute( | |
title: 'GridList', | |
), | |
MyRoute( | |
title: 'ExpansionTile', | |
), | |
MyRoute( | |
title: 'Swipe to dismiss', | |
), | |
MyRoute( | |
title: 'Reorderable list', | |
), | |
MyRoute( | |
title: 'DataTable', | |
description: 'Showing data in a table.', | |
), | |
], | |
), | |
MyRouteGroup( | |
groupName: 'Appbar', | |
icon: RotatedBox( | |
child: Icon(Icons.video_label), | |
quarterTurns: 2, | |
), | |
routes: <MyRoute>[ | |
MyRoute( | |
title: 'Basic AppBar', | |
), | |
MyRoute( | |
title: 'Bottom AppBar and Floating App Button (FAB)', | |
), | |
MyRoute( | |
title: 'Sliver AppBar', | |
description: 'Appbar that auto-hides.', | |
), | |
MyRoute( | |
title: 'Search', | |
), | |
MyRoute( | |
title: 'Backdrop', | |
description: 'Switching between front and back layer.', | |
), | |
], | |
), | |
MyRouteGroup( | |
groupName: 'Navigation', | |
icon: Icon(Icons.view_carousel), | |
routes: <MyRoute>[ | |
MyRoute( | |
title: 'Tabs', | |
), | |
MyRoute( | |
title: 'Dialogs', | |
), | |
MyRoute( | |
title: 'Routes', | |
description: 'Jumping between pages.', | |
), | |
MyRoute( | |
title: 'Navigation Drawer', | |
), | |
MyRoute( | |
title: 'Bottom sheet', | |
), | |
MyRoute( | |
title: 'Bottom tab bar', | |
), | |
MyRoute( | |
title: 'Bottom navigation bar', | |
), | |
MyRoute( | |
title: 'Page selector', | |
), | |
MyRoute( | |
title: 'DraggableScrollableSheet', | |
description: 'Scrollable sheet similar to Google Maps.', | |
), | |
], | |
), | |
MyRouteGroup( | |
groupName: 'Animation(basic)', | |
icon: Icon(Icons.movie_filter), | |
routes: <MyRoute>[ | |
MyRoute( | |
title: 'Hero', | |
description: 'Hero animation between routes.', | |
), | |
MyRoute( | |
title: 'Opacity', | |
description: 'Making a widget transparent/visible.', | |
), | |
MyRoute( | |
title: 'AnimatedContainer', | |
description: | |
'Implicit animation when container property changes, without controllers.', | |
), | |
], | |
), | |
]; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment