乐于分享
好东西不私藏

Build a Todo App with Flutter(1)

Build a Todo App with Flutter(1)

We will create a list of todos, when a todo is tapped, navigate to a new screen that displays information about the todo.

Define todo class

We need a simple way to represent todos. For this example, create a class that contains two pieces of data: the title and description.

classTodo{finalString title;finalString description;const Todo(this.title, this.description);}

Create todo list

We will generate 20 todos and show them using a ListView.

final todos = List.generate(20,  (i) => Todo('Todo $i','A description of what needs to be done for Todo $i',  ),);

ListView is a scrollable list of widgets arranged linearly.

ListView.builder(  itemCount: todos.length,  itemBuilder: (context, index) {return ListTile(title: Text(todos[index].title));  },)

ListView is the most commonly used scrolling widget. It displays its children one after another in the scroll direction. In the cross axis, the children are required to fill the ListView.

  • • If non-null, the itemExtent forces the children to have the given extent in the scroll direction.
  • • If non-null, the prototypeItem forces the children to have the same extent as the given widget in the scroll direction.

Specifying an itemExtent or an prototypeItem is more efficient than letting the children determine their own extent because the scrolling machinery can make use of the foreknowledge of the children’s extent to save work, for example when the scroll position changes drastically.

We can’t specify both itemExtent and prototypeItem, only one or none of them.

There are four options for constructing a ListView:

  1. 1. The default constructor takes an explicit List<Widget> of children. This constructor is appropriate for list views with a small number of children because constructing the List requires doing work for every child that could possibly be displayed in the list view instead of just those children that are actually visible.
  2. 2. The ListView.builder constructor takes an IndexedWidgetBuilder, which builds the children on demand. This constructor is appropriate for list views with a large (or infinite) number of children because the builder is called only for those children that are actually visible.
  3. 3. The ListView.separated constructor takes two IndexedWidgetBuilders:
    • • itemBuilder builds child items on demand.
    • • separatorBuilder similarly builds separator children which appear in between the child items. This constructor is appropriate for list views with a fixed number of children.
  4. 4. The ListView.custom constructor takes a SliverChildDelegate, which provides the ability to customize additional aspects of the child model. For example, a SliverChildDelegate can control the algorithm used to estimate the size of children that are not actually visible.

To control the initial scroll offset of the scroll view, provide a controller with its ScrollController.initialScrollOffset property set.

By default, ListView will automatically pad the list’s scrollable extremities to avoid partial obstructions indicated by MediaQuery‘s padding. To avoid this behavior, override with a zero padding property.

This example uses the default constructor for ListView which takes an explicit List<Widget> of children. This ListView‘s children are made up of Containers with Text.

ListView(  padding: const EdgeInsets.all(8),  children: <Widget>[    Container(      height: 50,      color: Colors.amber[600],      child: const Center(child: Text('Entry A')),    ),    Container(      height: 50,      color: Colors.amber[500],      child: const Center(child: Text('Entry B')),    ),    Container(      height: 50,      color: Colors.amber[100],      child: const Center(child: Text('Entry C')),    ),  ],)

Create todo screen

We can create a TodosScreen to display the todo list, since the contents of this page won’t change during runtime, we’ll have to require the list of todos within the scope of this widget.

If we pass in the ListView.builder as body of the TodosScreen widget, it will render the list on to this screen.

classTodosScreenextendsStatelessWidget{  // Requiring the list of todos.  const TodosScreen({super.key, required this.todos});  final List<Todo> todos;  @override  Widget build(BuildContext context) {return Scaffold(      appBar: AppBar(title: const Text('Todos')),//passing in the ListView.builder      body: ListView.builder(        itemCount: todos.length,        itemBuilder: (context, index) {return ListTile(title: Text(todos[index].title));        },      ),    );  }}

Create detail screen

We can create the screen with title of the specific todo and the body will show the description of this todo.

classDetailScreenextendsStatelessWidget{  // In the constructor, require a Todo.  const DetailScreen({super.key, required this.todo});  // Declare a field that holds the Todo.  final Todo todo;  @override  Widget build(BuildContext context) {// Use the Todo to create the UI.return Scaffold(      appBar: AppBar(title: Text(todo.title)),      body: Padding(        padding: const EdgeInsets.all(16),        child: Text(todo.description),      ),    );  }}

Navigate between screens

We can navigate to the DetailScreen when a user taps a todo in the list, pass the todo to the DetailScreen.

To capture the user’s tap in the TodosScreen, write an onTap() callback for the ListTile widget. Within the onTap() callback, use the Navigator.push() method.

body: ListView.builder(  itemCount: todos.length,  itemBuilder: (context, index) {return ListTile(      title: Text(todos[index].title),// When a user taps the ListTile, navigate to the DetailScreen.// Notice that you're not only creating a DetailScreen, you're// also passing the current todo through to it.      onTap: () {        Navigator.push(          context,          MaterialPageRoute<void>(            builder: (context) => DetailScreen(todo: todos[index]),          ),        );      },    );  },),

We can also pass the arguments using RoutingSettings.