这是我参与11月更文应战的第8天,活动详情检查:2021最后一次更文应战

json 转模型

class ChatModel {
  final String? name;
  final String? message;
  final String? imageUrl;
  ChatModel({this.name, this.message, this.imageUrl});
  //工厂结构办法
  factory ChatModel.fromMap(Map map) {
    return ChatModel(
      name: map['name'],
      message: map['message'],
      imageUrl: map['imageUrl'],
    );
  }
}
final chatMap = {
      'name' : 'ChenXi',
      'message' : 'Hello!',
    };
    //Map 转 json
    final chatJson = json.encode(chatMap);
    print(chatJson);
    // json 转 Map
    final newChatMap = json.decode(chatJson);
    print(chatJson);
    final chatModel = ChatModel.fromMap(newChatMap as Map);
    print(chatModel);

这儿咱们简略界说了一个 map 目标,代码示例中给出里 jsonMap 的彼此转化,及 Map 转模型。咱们界说了一个 ChatModel 的模型,添加了 fromMap 办法,由外部传入一个 Map 类型的目标。开源的也有一些转模型的结构,这儿咱们先自己完成。

Future 运用

void initState() {
    super.initState();
    //获取网络数据
    _getDatas().then((value) {
      print('$value');
    });
  }
  Future<List<ChatModel>> _getDatas() async {
    //url 链接
    final url = Uri.parse('http://rap2api.taobao.org/app/mock/294394/api/chat/list');
    //发送恳求
    final response = await http.get(url);
    if (response.statusCode == 200) {
      //获取响应数据,并且把 json 转成 Map
      final bodyMap = json.decode(response.body);
      // 取出 bodyMap 中的 chat_list 数组,通过 map 办法进行遍历并转为模型,通过 toList 回来一个模型数组
      final chatList = (bodyMap['chat_list'] as List).map((item) => ChatModel.fromMap(item)).toList();
      return chatList;
    } else {
      throw Exception('statusCode:${response.statusCode}');
    }

这儿 Future 代表未来的数据,Future 需要跟异步办法结合运用,咱们对回来的数据 Future<List<ChatModel>> 进行包装,Future 有一个 then 办法,then 有一个外部传入一个闭包特点,当数据恳求完成会调用闭包,这儿咱们能够拿到 value 的值,也便是模型数组。

FutureBuilder 异步烘托

Flutter 布局聊天列表页及网络数据处理

body: Container(
        child: FutureBuilder(
          future: _getDatas(),
          builder: (BuildContext context, AsyncSnapshot snapshot) {
            //正在加载
            if (snapshot.connectionState == ConnectionState.waiting) {
              return Container(
                child: Text('正在加载!'),
              );
            }
            //加载完成
            return ListView(
              children: snapshot.data.map<Widget>((ChatModel item) {
                return ListTile(
                  title: Text(item.name as String),
                  subtitle: Container(
                    alignment: Alignment.bottomCenter,
                    height: 25,
                    child: Text(item.message as String, overflow: TextOverflow.ellipsis,),
                  ),
                  leading: Container(
                    width: 44,
                    height: 44,
                    decoration: BoxDecoration(
                      borderRadius: BorderRadius.circular(6.0),
                      image: DecorationImage(image: NetworkImage(item.imageUrl as String)),
                    ),
                  ),
                );
              }).toList(),
            );
          },
        )
      )

这儿咱们通过 FutureBuilder 部件完成网络数据的加载,FutureBuilder 部件支撑异步烘托,future 特点是 Future 类型的数据。在每次进入微信页面的时分 builder 办法会被调用两次,ConnectionState.waiting 代表数据正在加载,在这儿咱们能够做一些空页面展现的处理。ConnectionState.done 代表数据加载完成,snapshot.data 便是 _getDatas 办法回来的列表数据,这儿能够进行相关逻辑的处理, 这儿咱们展现谈天列表数据。ListView 中咱们用 ListTile 部件来作为 cellListTile 包括主标题 title、副标题 subtitle、头像 leading 等特点,用起来很方便。

网络恳求数据处理

//模型数组
List<ChatModel> _datas = [];
void initState() {
    super.initState();
    //获取网络数据
    _getDatas().then((value) {
      if (!_cancelConnect) {
        setState(() {
          _datas = value;
        });
      }
    }).catchError((e) {
      _cancelConnect = true;
      //获取数据失利
      print(e);
    }).whenComplete(() {
      print('数据恳求结束');
    }).timeout(Duration(seconds: 5)).catchError((timeout) {
      _cancelConnect = true;
      print('恳求超时 ! $timeout');
    });
  }

这儿咱们创建了一个外部成员变量 _datas,用来保存网络恳求的数据,网络恳求成功之后调用 setState 办法,界说了一个特点 _cancelConnect 标识网络恳求是否取消。catchError 代表恳求失利,whenComplete 代表恳求结束,timeout 能够设置超时时刻,这儿咱们能够用来做一下 loading 页面的展现及过错页面的展现,

Container(
          child: _datas.length == 0 ? Center(child: Text('Loading...')) :
          ListView.builder(itemCount: _datas.length ,itemBuilder: (BuildContext context, int index) {
            return ListTile(
              title: Text(_datas[index].name as String),
              subtitle: Container(
                alignment: Alignment.bottomCenter,
                height: 25,
                child: Text(_datas[index].message as String, overflow: TextOverflow.ellipsis,),
              ),
              leading: Container(
                width: 44,
                height: 44,
                decoration: BoxDecoration(
                  borderRadius: BorderRadius.circular(6.0),
                  image: DecorationImage(image: NetworkImage(_datas[index].imageUrl as String)),
                ),
              ),
            );
          }),
        )

关于列表的展现咱们这儿换回了 ListView,运用 FutureBuilder,数据量比较大的话会影响加载问题。

页面坚持状态

class _ChatPageState extends State<ChatPage> with AutomaticKeepAliveClientMixin<ChatPage>
Widget build(BuildContext context) {
    super.build(context);
}
class _RootPageState extends State<RootPage> {
  int _currentIndex = 0;
  List <Widget>_pages = [ChatPage(), FriendsPage(), DiscoverPage(), MinePage()];
  final PageController _controller = PageController();
  @override
  Widget build(BuildContext context) {
    return Container(
      child: Scaffold(
        body: PageView(
          //制止页面拖拽
          physics: NeverScrollableScrollPhysics(),
          onPageChanged: (int index) {
            setState(() {
              _currentIndex = index;
            });
          },
          controller: _controller,
          children: _pages,
        ),

当咱们切换底部 tabBar 的时分,每次进入页面都会从头加载,这儿咱们选用 AutomaticKeepAliveClientMixin 来坚持状态,让页面只会被加载一次,以谈天页面为例,_ChatPageState 后边加上 with AutomaticKeepAliveClientMixin<ChatPage>,并在 build 办法中调用 super.build(context)。在 RootPage 中,用 _pages 数组来保存底部子页面,body 运用 PageView 部件,controller 赋值为咱们界说的 _controllerchildren 赋值为 _pages