“This is the 7th day of my participation in the Gwen Challenge in November. Check out the details: The Last Gwen Challenge in 2021.”

review

Last time we got the data, this chapter is mainly about how to display the data. Data display inevitably involves asynchronous loading and data conversion

Json to a dictionary

Json data in a Flutter can be converted to a dictionary using json.decode

import 'dart:convert';
json.decode(response.body);
Copy the code

Dictionaries are transformed into models

The constructor needs to use the factory method if it wants to have a return value

class ChatModel {
  final String? imgUrl;
  final String? name;
  final String? message;
  ChatModel({this.imgUrl, this.name, this.message});
  factory ChatModel.fromMap(Map map) {
    return ChatModel(
        imgUrl: map['imgUrl'], name: map['name'], message: map['message']); }}Copy the code

Model data source

For asynchronously loaded data, get the return values of all the data, and use the Future

  Future<List<ChatModel>> getData() async {
    final url =
        Uri.parse('http://rap2api.taobao.org/app/mock/293759/home/chat/list');
    var response = await http.get(url);
    if (response.statusCode == 200) {
      // json to a dictionary
      final responseBody = json.decode(response.body);
      return responseBody['data']
          .map<ChatModel>((item) => ChatModel.fromMap(item))
          .toList();
      / / model
    } else {
      throw Exception('statusCode=${response.statusCode}'); }}}Copy the code

Apply colours to a drawing

There’s a FutureBuilder for rendering asynchronously loaded data, there’s a mandatory parameter Builder, Typedef AsyncWidgetBuilder

= Widget Function(BuildContext context, AsyncSnapshot

snapshot);

Container(
  child: FutureBuilder(
     builder: (BuildContext context, AsyncSnapshot snapshot) {
       print('${snapshot.data}');
       return Container();
    },
    future: getData(),
)),
Copy the code

When printing snapshot.data, there will be two steps. One is null when the page is loaded, and the second is to refresh the data again after the array is retrieved

When the snapshot. ConnectionState: waiting without the data, when done shows that data have finished loading.

So at this point we can use thisconnectionStateTo judge the state of

if (snapshot.connectionState == ConnectionState.waiting) {
       return Center(child: Text('Loading... '));
}
Copy the code

The children of the ListView can be generated by traversing the snapshot.data.map

((ChatModel item) array, and a new Widget is introduced here: The ListTile contains simple, commonly used layouts

const ListTile({
    Key? key,
    this.leading,
    this.title,
    this.subtitle,
    this.trailing,
    this.isThreeLine = false.this.dense,
    this.visualDensity,
    this.shape,
    this.contentPadding,
    this.enabled = true.this.onTap,
    this.onLongPress,
    this.mouseCursor,
    this.selected = false.this.focusColor,
    this.hoverColor,
    this.focusNode,
    this.autofocus = false.this.tileColor,
    this.selectedTileColor,
    this.enableFeedback,
    this.horizontalTitleGap,
    this.minVerticalPadding,
    this.minLeadingWidth,
  }) : assert(isThreeLine ! =null),
       assert(enabled ! =null),
       assert(selected ! =null),
       assert(autofocus ! =null),
       assert(! isThreeLine || subtitle ! =null),
       super(key: key);
Copy the code

The layout looks like this:

State reserve

There was a slight problem with the data being reloaded every time the page was cut out and then cut back. In this case, the state is not preserved. If you want to keep it:

  1. inheritanceAutomaticKeepAliveClientMixin

class _ChatPageState extends State<ChatPage> with AutomaticKeepAliveClientMixin

  1. Then implement the methods of the parent class
  @override
  bool get wantKeepAlive => true;
Copy the code
  1. inWidget build(BuildContext context)In the implementationsuper.build(context);

Modify the Widget tree

Despite the implementation of the above three steps, it is still init again when cutting back. This is because each page is regenerated under the root controller and not in the current Widget tree. We will go back to the RootPage page to make changes

  1. Declare afinal PageController _pageController = PageController();
  2. bodyusePageView
body: PageView(
  children: _pages,
  controller: _pageController,
),
Copy the code
  1. When I switch pages,_pageControllerSynchronous jump
setState(() {
   _currentIndex = index;
   _pageController.jumpToPage(_currentIndex);
});
Copy the code

This saves the current page state every time you click on a TabbarItem. There is a slight problem, though. The current page can be swiped, but the buttons at the bottom don’t work with it. This minor problem can be solved in PageView onPageChanged:

onPageChanged: (index) {
  setState(() {
     _currentIndex = index;
   });
},
Copy the code

Cut or setting cannot drag screen: physics: NeverScrollableScrollPhysics (),

Future

The Future was used in the network request above, so what is this?

String _temp = '0';

void main() {
  // operatorDemo();
  getData();
  print('Before loop');
}

getData() {
  print('Here we go.');
  Future(() {
    for (int i = 0; i < 100; i++) {}
    print('End of loop');
  });
}
Copy the code

Found to be used after testingFutureThe modified code block executes asynchronously and does not choke the current thread. If you want to wait until the asynchronous task is complete, you need to run theFutureI’ll put one in front of itawait

String _temp = '0';

void main() {
  // operatorDemo();
  getData();
  print('Before loop');
}

getData() async {
  print('Here we go.');
  await Future(() {
    for (int i = 0; i < 100; i++) {}
    print('End of loop');
  });
  print('Code after await');
}
Copy the code

Full code address: