Skip to content

Instantly share code, notes, and snippets.

@stegrams
Last active May 20, 2020 21:12
Show Gist options
  • Save stegrams/02dc9d480ed2e01e81341d34b23800b4 to your computer and use it in GitHub Desktop.
Save stegrams/02dc9d480ed2e01e81341d34b23800b4 to your computer and use it in GitHub Desktop.
X-Wei/flutter_catalog basic demos GUI for ListView study.
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