MVVM重构Flutter Mall

作者: A路由新 | 来源:发表于2020-05-30 09:40 被阅读0次

    去年9月份发布了flutter_mall 的第一个版本到现在差不多有9个月了,到现在已经获得了1.2K star,好几个月没有写flutter了,最近回头看了下之前的代码,发现有很多地方可以进行优化,综合考虑以后决定用MVVM重构,并对bug进行修复。

    对于什么是MVVM在这里我就不再说明, 只给出如下图,自行体会

    image

    Provider是基于InheritedWidget实现的一套跨组件状态共享解决方案,这样就可以Model改变的时候刷新View显示的数据。

    新建flutter_mvvm项目


    image
    文件夹 作用
    constant 主要是放一些常量
    models 后台返回的数据模型
    views 页面
    widgets 封装的自定义widget
    utils 工具类
    view_models viewmodel

    这里以获取商品列表为列

    使用代码生成库序列化JSON采用框架json_serializable,没有使用过的可以看下具体的教程,非常方便的一个框架。

    goods_model.dart

    import 'package:json_annotation/json_annotation.dart';
    
    part 'goods_model.g.dart';
    
    class GoodListModel {
      List<GoodsModel> goodsEntitys;
    
      GoodListModel(this.goodsEntitys);
    
      factory GoodListModel.fromJson(List<dynamic> parseJson) {
        List<GoodsModel> goodsEntitys;
        goodsEntitys = parseJson.map((i) => GoodsModel.fromJson(i)).toList();
        return GoodListModel(goodsEntitys);
      }
    }
    
    @JsonSerializable()
    class GoodsModel extends Object {
      @JsonKey(name: 'id')
      int id;
    
      @JsonKey(name: 'name')
      String name;
    
      @JsonKey(name: 'brief')
      String brief;
    
      @JsonKey(name: 'picUrl')
      String picUrl;
    
      @JsonKey(name: 'isNew')
      bool isNew;
    
      @JsonKey(name: 'isHot')
      bool isHot;
    
      @JsonKey(name: 'counterPrice')
      double counterPrice;
    
      @JsonKey(name: 'retailPrice')
      double retailPrice;
    
      GoodsModel(
        this.id,
        this.name,
        this.brief,
        this.picUrl,
        this.isNew,
        this.isHot,
        this.counterPrice,
        this.retailPrice,
      );
    
      factory GoodsModel.fromJson(Map<String, dynamic> srcJson) =>
          _$GoodsModelFromJson(srcJson);
    
      Map<String, dynamic> toJson() => _$GoodsModelToJson(this);
    }
    
    

    网络框架dio,并进行二次封装,用单例模式,并添加拦截器打印请求和返回的数据

    import 'package:dio/dio.dart';
    
    var dio;
    
    class HttpUtil {
      // 工厂模式
      static HttpUtil get instance => _getInstance();
    
      static HttpUtil _httpUtil;
    
      static HttpUtil _getInstance() {
        if (_httpUtil == null) {
          _httpUtil = HttpUtil();
        }
        return _httpUtil;
      }
    
      HttpUtil() {
        BaseOptions options = BaseOptions(
          connectTimeout: 5000,
          receiveTimeout: 5000,
        );
        dio = new Dio(options);
        dio.interceptors
            .add(InterceptorsWrapper(onRequest: (RequestOptions options) {
          print("========================请求数据===================");
          print("url=${options.uri.toString()}");
          print("params=${options.data}");
    
          return options;
        }, onResponse: (Response response) {
          print("========================请求数据===================");
          print("code=${response.statusCode}");
          print("response=${response.data}");
        }, onError: (DioError error) {
          print("========================请求错误===================");
          print("message =${error.message}");
        }));
      }
    
      Future get(String url, {Map<String, dynamic> parameters, Options options}) async {
        Response response;
        if (parameters != null && options != null) {
          response = await dio.get(url, queryParameters: parameters, options: options);
        } else if (parameters != null && options == null) {
          response = await dio.get(url, queryParameters: parameters);
        } else if (parameters == null && options != null) {
          response = await dio.get(url, options: options);
        } else {
          response = await dio.get(url);
        }
        return response.data;
      }
    
      Future post(String url,
          {Map<String, dynamic> parameters,Options options}) async {
        Response response;
        if (parameters != null && options != null) {
          response = await dio.post(url, data: parameters, options: options);
        } else if (parameters != null && options == null) {
          response = await dio.post(url, data: parameters);
        } else if (parameters == null && options != null) {
          response = await dio.post(url, options: options);
        } else {
          response = await dio.post(url);
        }
        return response.data;
      }
    }
    
    

    定义网络请求返回处理结果的一些回调

    typedef OnSuccessList<T>(List<T> banners);
    
    typedef OnFail(String message);
    
    typedef OnSuccess<T>(T onSuccess);
    

    请求网络并处理返回数据

     Future getCategoryGoodsListData(
          Map<String, dynamic> parameters, OnSuccessList onSuccessList,
          {OnFail onFail}) async {
        try {
          var responseList = [];
          var response = await HttpUtil.instance
              .get(Api.GOODS_LIST_URL, parameters: parameters);
          if (response[Strings.HTTP_ERROR_NO] == 0) {
            responseList = response[Strings.HTTP_DATA][Strings.HTTP_LIST];
            GoodListModel goodListModel = GoodListModel.fromJson(responseList);
            onSuccessList(goodListModel.goodsEntitys);
          } else {
            onFail(response[Strings.HTTP_ERROR_MSG]);
          }
        } catch (e) {
          print(e);
          onFail(Strings.SERVER_EXCEPTION);
        }
      }
    

    GoodsListModel中请求列表数据并在数据变化的时候通知view进行刷新

    class GoodsListViewModel extends ChangeNotifier {
      GoodsService goodsService = GoodsService();
      List<GoodsModel> goodsData;
      bool hasData;
      void loadGoodsData(String categoryId, int page, int limit) async {
        var parameters = {"categoryId": categoryId, "page": page, "limit": limit};
        goodsService.getCategoryGoodsListData(parameters, (goodsList) {
          if (page == 1) {
            goodsData = goodsList;
            notifyListeners();
            return;
          }
          List<GoodsModel> data = goodsList;
          goodsData.addAll(data);
          notifyListeners();
        }, onFail: (message) {
         // ToastWidget.showToast(message);
          hasData = !(goodsData == null || goodsData.length == 0);
          notifyListeners();
        });
      }
    
    }
    

    在数据变化以后刷新列表

    class MyHomePage extends StatefulWidget {
      MyHomePage({Key key, this.title}) : super(key: key);
    
      final String title;
    
      @override
      _MyHomePageState createState() => _MyHomePageState();
    }
    
    class _MyHomePageState extends State<MyHomePage> {
      GoodsListViewModel model = GoodsListViewModel();
      AppThemeViewModel themeViewModel = AppThemeViewModel();
    
      @override
      void initState() {
        super.initState();
        model.loadGoodsData("1008002", 1, 100);
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
            appBar: AppBar(
              title: Text(Strings.MVVM),
            ),
            body: Center(
              child: ChangeNotifierProvider<GoodsListViewModel>(
                create: (context) => model,
                child:
                    Consumer<GoodsListViewModel>(builder: (context, model, child) {
                  return ListView.builder(
                      itemCount:
                          model.goodsData == null ? 0 : model.goodsData.length,
                      itemBuilder: (BuildContext context, int index) {
                        return InkWell(
                          child: Text(model.goodsData[index].name),
                          onTap: () {
                          Provider.of<AppThemeViewModel>(context,listen: false).changeTheme();
                          },
                        );
                      });
                }),
              ),
            ));
      }
    }
    

    到这里用mvvm实现一个简单商品列表页面就成功了。

    相关文章

      网友评论

        本文标题:MVVM重构Flutter Mall

        本文链接:https://www.haomeiwen.com/subject/tsiwahtx.html