FutureBuilder to achieve lazy loading, and can listen to the loading process of the state of the Demo is loaded to play Android a page list data

1. Requirement scenario

Often have these scenes, is to request network load data and display the chrysanthemum, after obtaining the data according to the request according to the results of different interface, such as error request, show that the error interface, the inside of the response result if data list is empty, display data for empty interface, have the data, the tabular data for display loaded into the list.

2. Required controls

  • Drop down refresh refreshIndicators, list ListView, I won’t go into too much here
  • FutureBuilder: because of the application of the asynchronous model, based on the latest snapshot to build their own Future interaction and widget official document: docs. Flutter. IO/Flutter/wid…
Const FutureBuilder({Key Key, this.future,// method to get data this.InitialData, @required this.builder// Return different widgets based on snapshot state}) : assert(builder ! = null), super(key: key);Copy the code

Future is a defined asynchronous operation. If the snapshot is dynamic, the snapshot will be a snapshot of the state of the asynchronous operation. There are four snapshot states to load different widgets based on their connectionState:

Enum ConnectionState {// Future Not yet executed snapshot state None, // Connect to an asynchronous operation and wait for the interaction, in this state we can load a chrysanthemum waiting, // connect to an active operation such as stream, The result of the asynchronous operation can be retrieved from this position and the corresponding layout is displayeddone,}Copy the code

Here’s the official example.

FutureBuilder<String>(
  future: _calculation, // a previously-obtained Future<String> or null
  builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
    switch (snapshot.connectionState) {
      case ConnectionState.none:
        return Text('Press button to start.');
      case ConnectionState.active:
      case ConnectionState.waiting:
        return Text('Awaiting result... ');
      case ConnectionState.done:
        if (snapshot.hasError)
          return Text('Error: ${snapshot.error}');
        return Text('Result: ${snapshot.data}');
    }
    return null; // unreachable
  },
)
Copy the code

3. Realization of ideas and layout

  • Network request: using the Dio library to request to play list, the knowledge of the Android system api:www.wanandroid.com/tree/json

  • Serialize JSON: Parse returned JSON data using jSON_serialIZABLE

  • Layout: loading process shows CircularProgressIndicator, loaded to display the data in the ListView, loading is empty or loading error, according to the corresponding page, and can retry the request In general. Our list page has the drop-down refresh function, so RefreshIndicator is used here.

4. Code implementation

import 'package:flutter/material.dart';
import 'package:dio/dio.dart';
import 'entity.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        // This is the theme of your application.
        //
        // Try running your application with "flutter run". You'll see the // application has a blue toolbar. Then, without quitting the app, try // changing the primarySwatch below to Colors.green and then invoke // "hot reload" (press "r" in the console where you ran "flutter run", // or simply save your changes to "hot reload" in a Flutter IDE). // Notice that the counter didn't reset back to zero; the application
        // is not restarted.
        primarySwatch: Colors.blue,
      ),
      home: FutureBuilderPage(),
    );
  }
}

class FutureBuilderPage extends StatefulWidget {
  @override
  _FutureBuilderPageState createState() => _FutureBuilderPageState();
}

class _FutureBuilderPageState extends State<FutureBuilderPage> {
  Future future;

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    future = getdata();
  }

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text("Body of knowledge"),
        actions: <Widget>[
          new IconButton(
              icon: Icon(
                Icons.search,
                color: Colors.white,
              ),
              onPressed: null)
        ],
      ),
      body: buildFutureBuilder(),
      floatingActionButton: new FloatingActionButton(onPressed: () {
        setState(() {// Test whether FutureBuilder does unnecessary redraw}); })); } FutureBuilder<List<Data>>buildFutureBuilder() {
    returnNew FutureBuilder<List<Data>>(Builder: (context, AsyncSnapshot<List<Data>> async) {//if (async.connectionState == ConnectionState.active ||
            async.connectionState == ConnectionState.waiting) {
          return new Center(
            child: new CircularProgressIndicator(),
          );
        }
        if (async.connectionState == ConnectionState.done) {
          debugPrint("done");
          if (async.hasError) {
            return new Center(
              child: new Text("ERROR")); }else if (async.hasData) {
            List<Data> list = async.data;
            return new RefreshIndicator(
                child: buildListView(context, list),
                onRefresh: refresh);
          }
        }
      },
      future: future,
    );
  }

  buildListView(BuildContext context, List<Data> list) {
    return new ListView.builder(
      itemBuilder: (context, index) {
        Data bean = list[index];
        StringBuffer str = new StringBuffer();
        for (Children children in bean.children) {
          str.write(children.name + "");
        }
        returnnew ListTile( title: new Text(bean.name), subtitle: new Text(str.toString()), trailing: new IconButton( icon: new Icon( Icons.navigate_next, color: Colors.grey, ), onPressed: () {}), ); }, itemCount: list.length, ); } // Get data logic, use dio library for network request, Future<List<Data>> GetData () Async {debugPrint()"getdata");
    var dio = new Dio();
    Response response = await dio.get("http://www.wanandroid.com/tree/json");
    Map<String, dynamic> map = response.data;
    Entity entity = Entity.fromJson(map);
    returnentity.data; Future refresh() async {setState(() { future = getdata(); }); }}Copy the code

5. Attention to problems and trampling pits

  • Prevent Unnecessary redrawing of FutureBuilder: The method I use here is to assign getData () to a future member variable that holds the result of getData () to avoid unnecessary redrawing. Blog.csdn.net/u011272795/…
  • [bug Mc-102128] – Nesting of the FutureBuilder and RefreshIndicator, who is the child of the other? If the RefreshIndicator is placed outside and FutureBuilder is the Child, when the RefreshIndicator calls onrefreh to refresh the data and setState () to update the interface, The FutureBuilder will also go through the life cycle again, so the logic to get the data will go through twice

6. Next, wrap a generic ListView

Because this scenario is used a lot, it’s a lot of code to write all the time, and it’s a lot of trouble. So the next step is to encapsulate a generic ListView with basic pull-down refresh and pull-up load capabilities.

7. Relevant codes:

Github.com/LXD31256949…