flutter 项目实战4

这篇文章来自于我自己的有道云笔记 想看图片去那里
文档:Day 4_8 项目实战以及 国际化.md
链接:http://note.youdao.com/noteshare?id=9a784492b39cbcd20f152885d183c884&sub=2193AB9CCC944AEAA4EA1EC8B8A0BB03

项目实战以及 国际化

问题1

点击左边 然后刷新右边的数据 就是 如何进行组件间的信息传递

我们可以用 事件总线来完成 这个事件的传递

也可以使用Provider完成这个操作

因为这个数据 只有一边会使用 另外一边只是改动

所以我们使用事件总线会好一点

虽然我们的项目比较小 但是我们的东西 是比较全的

问题2

我们的Drawer我们发现它其实是因该将这个 tabbar给覆盖上的

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YvE62le6-1589850162076)(E6CF70B0B0884D22A327D13910BC0BEC)]

其实也是能做的

之前我们做的时候是将Drawer放在HomeScreen里面的

我们的HomeScreen是只有 中间的部分

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4zOA4gMv-1589850162083)(2D133EFCF4BC4726B1DBBF32B8EA413E)]

所以是只能控制上面的部分

所以如果你想让 下面的部分也盖上的话 我们就需要将这个Drawer把上面的也盖上

我们就需要将Drawer也放到main里面
我们来到这个Drawer里面将 这个放到里面来

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1RI9CQIy-1589850162087)(4D3DEE95DF3F4FDDB29B9FB7686C7ED4)]

那我们怎么把这个窗口弹出呢

我们其实点击这个HomeScreen的appBar的leading来将这个 弹出的

这个leading是属于 这个HomeScreen 那我们怎么来做呢

我们 来到这个home_app_bar.dart

import "package:flutter/material.dart";

class HYHomeAppBar extends AppBar {
  HYHomeAppBar(BuildContext context):super(
    title: Text("美食广场"),
    leading: Builder(
        builder: (BuildContext context) {
          return IconButton(
            icon: Icon(Icons.build),
            onPressed: () {
//                  它这里的目的是为了拿到这个 Scaffold 然后调用它的一个openDrawer
//                 这样来做的
              Scaffold.of(context).openDrawer();
            },
          );
        }
    ),
  );
}

以前我们的做法是找到对应的Scaffold里面的Drawer 但是现在我们找的不是这里的Scaffold 而是要找外面的Scaffold 所以我们这里把这个 Builder给去掉就可以实现这个功能了

import "package:flutter/material.dart";

class HYHomeAppBar extends AppBar {
  HYHomeAppBar(BuildContext context):super(
    title: Text("美食广场"),
    leading: IconButton(
      icon: Icon(Icons.build),
      onPressed: () {
//                  它这里的目的是为了拿到这个 Scaffold 然后调用它的一个openDrawer
//                 这样来做的
        Scaffold.of(context).openDrawer();
      },
    )
  );
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RTyNX1lP-1589850162132)(7A6E74E53507469F956955B5AB1B52D3)]

这样我们就可以 弹出这个 Drawer了

我们只要去拿上一层的Scaffold就行了

过滤页面

然后就是 做一个过滤的页面

filter-> filter.dart

import "package:flutter/material.dart";
import 'package:project03/ui/pages/filter/filter_content.dart';

class HYFilterScreen extends StatelessWidget {
  static const String routeName = "/filter";

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("美食过滤"),
      ),
      body: HYFilterContent(),
    );
  }
}

filter -> filter_content.dart

import "package:flutter/material.dart";

class HYFilterContent extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Text("test");
  }
}

然后就是在对应的位置弹出这个页面

home_drawer.dart

  Widget build(BuildContext context) {
    return Container(
      width: 250.px,
      child: Drawer(
        child: Column(
          children: [
            buildHeaderView(context),
            buildListTile(context, Icon(Icons.restaurant), "进餐", () {
              Navigator.of(context).pop();
            }),
            buildListTile(context, Icon(Icons.settings), "过滤", () {
              Navigator.of(context).pushNamed(routeName);
            })
          ],
        ),
      )
    );
  }

然后就是配置路由 但是如果这里我们配置路由的话

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hlemeWRD-1589850162134)(5B0EE73A6D2144C78E1BDD73159FF782)]

他的页面弹出方式就是从右往左 那如果你想自定义 来完成这个操作

我们可以在generateRoute来识别这个路由

//  自己扩展
  static final RouteFactory generateRoute = (settings) {
    if(settings.name == ) {
      
    }
    return null;
  };

所以我们将这个识别以后包裹一个MaterialPageRoute

  static final RouteFactory generateRoute = (settings) {
    if(settings.name == HYFilterScreen.routeName) {
      return MaterialPageRoute(
        builder: (ctx) {
          return HYFilterScreen();
        },
        fullscreenDialog: true
      );
    }
    return null;
  };

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SY5ljuCW-1589850162137)(7C4DD1BB8471485EB77CB04845E4667F)]

这样我们就能这样弹出页面了

我们来做这个看这个怎么做

上面的标题是固定死 然后这个是可以滚动的

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lHBRoD6F-1589850162138)(9138A5EF4F8446519962683B894E20B2)]

import "package:flutter/material.dart";

import "../../../core/extension/int_extension.dart";

class HYFilterContent extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        buildYourChoice(context),
      ],
    );
  }

  Widget buildYourChoice(BuildContext context) {
    return Container(
      padding: EdgeInsets.all(20.px),
      alignment: Alignment.center,
      child: Text("展示你的选择", style: Theme.of(context).textTheme.display3.copyWith(fontWeight: FontWeight.bold)),
    );
  }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kyNn76Uz-1589850162140)(356C37DEE94641ECA7F727A802789483)]

标题

ListView滚动区域

  Widget buildChoiceSelect() {
    return ListView(
      children: [],
    );
  }

这个里面都是一些固定的内容 所以我们就不生成了

然后我们往里面放东西

但是这里是一定会报错的

  Widget buildChoiceSelect() {
    return ListView(
      children: [
        Text("aaa"),
        Text("bbbb"),
        Text("ccc"),
        Text("aaa"),
        Text("aaa"),
      ],
    );
  }

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vd9TAHqt-1589850162141)(2532C6FCDF6741259F49E67998410839)]

又是这个hasSize同样我们的 父Widget需要子Widget穿过来大小 子Widget 它要站尽可能多的位置 所以这里就报错了

外面是一个Column里面有一个ListView 就出错了

那我们怎么办呢 我们给ListView设置一个包裹的属性 这样它就不会占据尽可能大的位置

  Widget buildChoiceSelect() {
    return ListView(
      shrinkWrap: true,
      children: [
        Text("aaa"),
        Text("bbbb"),
        Text("ccc"),
        Text("aaa"),
        Text("aaa"),
      ],
    );
  }

但是这个东西 它一般用在 制作列表的时候

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iUtbQsMV-1589850162143)(A2FD36DEDFFF4F3EB3C5A4C66A0CE4EE)]

但是这里我们是希望占据 剩下的位置

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gQBGmiUE-1589850162144)(7F97F7C7A3D44E80BA8EE73E0FAFCA65)]

这里就不要使用这种方案了 我们给它包裹一个Expend

我们给他 包裹一个Expanded

如果这个东西会将这个 内容延长到 剩余的空间 如果超过了就会压缩你的内容

  Widget buildChoiceSelect() {
    return Expanded(
      child: ListView(
        children: [
          Text("aaa"),
          Text("bbbb"),
          Text("ccc"),
          Text("aaa"),
          Text("aaa"),
        ],
      ),
    );
  }

它会占据你垂直方向上的整个的空间

怎么证明呢

这里

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tLgKkKuQ-1589850162145)(D71366C762F84D5A9FD6FE0A0595BCC2)]

拖动的动画都延续到最下面了

我们肯定不是用这个Text

我们是用ListTile leading是放在前面的东西 traling就代表我尾部

  Widget buildChoiceSelect() {
    return Expanded(
      child: ListView(
        children: [
          ListTile(
            title: Text("五谷蛋白"),
            subtitle: Text("五谷蛋白"),
            trailing: Switch(
              value: false,
              onChanged: (value) {

              },
            ),
          )
        ],
      ),
    );
  }

但是这个东西因为会站很多地方会用 所以我们将这个东西封装一下

  Widget buildChoiceSelect() {
    return Expanded(
      child: ListView(
        children: [
          buildListTile("五谷蛋白", "五谷蛋白", (value) {

          }),
          buildListTile("不含乳糖", "不含乳糖", (value) {

          }),
          buildListTile("素食主义", "素食主义", (value) {

          }),
          buildListTile("严格的素食主义", "严格的素食主义", (value) {

          }),
        ],
      ),
    );
  }

  Widget buildListTile(String title, String subtitle, Function onChange) {
    return ListTile(
      title: Text(title),
      subtitle: Text(subtitle),
      trailing: Switch(
        value: false,
        onChanged: onChange,
      ),
    );
  }

我们这个选中了以后 它就会过滤选中的如果 对应的属性没有

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6JXTHZ7s-1589850162146)(557D8741493F482883FE8448B06F8C34)]

这样就是 如果 你选中了不含乳糖 那么 这个选项就不会被过滤

我们到时候 选中对应的过滤的东西 我们首先要需要保存 这些bool类型的值

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Gsq9y2BW-1589850162148)(FF36CFCC7B2148C19CC21E1751B1371A)]

而且他们还要 在不同的页面之前共享 所以我们就压把它放在Provider中

我们可以将这些数据 放到 MealViewModel一起保存

这样我们就只需要添加一些属性在 MealViewModel中即可 但是这里我们 数据 收藏地方也希望用到

这样的话我们的收藏就需要依赖这个Meal的Provider 其实也可以做

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cbYWQ0id-1589850162150)(26E5A30AC56044059AC30934577EB09D)]

这样来做它的耦合性就太高了 所以 这个bool值直接放到这里是不合适的

我们最好在搞一个FilterViewModel

让MealViewModel和FavorViewModel都依赖于这个FilterViewModel就可以了

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-61Taxu1S-1589850162152)(53C7C1AFECDF4CD88D639082F57F5054)]

MealViewModel和FavorViewModel 之间就没有依赖了

这样我们就可以

我们新建一个ViewModel.dart

然后 alt + insert 生成getter和setter

filter_view_model.dart

import 'package:flutter/material.dart';

class HYFilterViewModel extends ChangeNotifier {
//  五谷蛋白
  bool _isGlutenFree = false;
//  素食主义
  bool _isVegan = false;
//  严格的素食主义
  bool _isVegetarian = false;
//  有无乳糖
  bool _isLactoseFree = false;

  bool get isGlutenFree => _isGlutenFree;

  bool get isLactoseFree => _isLactoseFree;

  bool get isVegetarian => _isVegetarian;

  bool get isVegan => _isVegan;

  set isGlutenFree(bool value) {
    _isGlutenFree = value;
    notifyListeners();
  }

  set isLactoseFree(bool value) {
    _isLactoseFree = value;
    notifyListeners();
  }

  set isVegetarian(bool value) {
    _isVegetarian = value;
    notifyListeners();
  }

  set isVegan(bool value) {
    _isVegan = value;
    notifyListeners();
  }
}

这样我们就封装好了这个东西了

然后我们就需要在filter_content.dart里面使用

所以这里我们对他做操作

因为我们这里如果选择了 对应的按钮他因该是true的但是我们这里封装的 因该只有false

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kGZtxuWd-1589850162153)(0140C0EEBE7E4AADB9835118EE602BC1)]

我把这个东西给你产生对应的参数

传到里面来显示 同时在点击以后修改

filter_content.dart

  Widget buildChoiceSelect() {
    return Expanded(
      child: Consumer(
        builder: (ctx, filterVM, child) {
          return ListView(
            children: [
              buildListTile("五谷蛋白", "五谷蛋白", filterVM.isGlutenFree, (value) {
                filterVM.isGlutenFree = value;
              }),
              buildListTile("不含乳糖", "不含乳糖", filterVM.isLactoseFree, (value) {
                filterVM.isLactoseFree = value;
              }),
              buildListTile("素食主义", "素食主义", filterVM.isVegan, (value) {
                filterVM.isVegan = value;
              }),
              buildListTile("严格的素食主义", "严格的素食主义", filterVM.isVegetarian, (value) {
                filterVM.isVegetarian = value;
              }),
            ],
          );
        }
      ),
    );
  }

  Widget buildListTile(String title, String subtitle, bool value, Function onChange) {
    return ListTile(
      title: Text(title),
      subtitle: Text(subtitle),
      trailing: Switch(
        value: value,
        onChanged: onChange,
      ),
    );
  }

同时要将这个Provider放到对应的依赖里面去

main.dart

void main() {
//  Provider -> ViewModel / Provider / Consumer(Selector)
  runApp(
      MultiProvider(
        providers: [
          ChangeNotifierProvider(
            create: (ctx) => HYMealViewModel(),
          ),
          ChangeNotifierProvider(
            create: (ctx) => HYFavorViewModel(),
          ),
          ChangeNotifierProvider(
            create: (ctx) => HYFilterViewModel(),
          )
        ],
        child: MyApp(),
      )
  );
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DnRYdE56-1589850162155)(8412533A44C5406B8C5C5EF9501A8B5A)]

这个时候我们就将这个内容在provider里面做了一个记录了

所以我们就需要用这个数据来对我们的数据来做一个过滤

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vLcV29Sg-1589850162156)(9428155FA6DB45B5A391A2AC911C7818)]

这里我们就需要在meal_view_model里面使用filter_view_model里面的东西

这里我们就需要知道在meal_view_model里面 依赖filter_view_model

我们来到main.dart根目录

我们使用ChangeNotifierProxyProvider这个东西

  • Proxy就是代理的意思

这里我们就把这个 HYMealViewMode给删掉了

          ChangeNotifierProxyProvider(
            create: (ctx) => HYMealViewModel(),
            update: ,
          ),

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qR5p8sdd-1589850162157)(82058C36988A4BBA9C37C5381D615BB0)]

这个updata是必传

这个 ChangeNotifierProxyProvider 是一个泛型类 他需要传两个泛型

我们的ChangeNotifierProvider 它也是一个泛型类 但是它只有一个参数 我们一般是不穿这个参数的

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fu5PDChH-1589850162158)(F0D9A848DA164FED9AE261BAF5993C3A)]

为什么呢 因为你在最后返回的时候它会返回一个参数 这个东西就他的类型

所以这里我们一般都不传

但是这个ChangeNotifierProxyProvider它需要你传两个 所以你需要 中间有一个依赖

这两个泛型第一个是 要依赖的泛型

第二个是要返回的ViewModel

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PQr1JcUU-1589850162159)(6AF58BBF632C47288296E251E93E59BF)]

这两个泛型是那里用的呢

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0HFBUyLy-1589850162161)(B7DE6342398741AFBE605C40F442F9F6)]

它是在update里面的

这个两个泛型又是在函数中传递参数的时候使用

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WIRdJ98a-1589850162163)(BB73A0C6674F472D92DA562C521A5058)]

所以我们这里要这么写

          ChangeNotifierProxyProvider(
            create: (ctx) => HYMealViewModel(),
            update: (ctx, filterVM, mealVM) {
              return 
            },
          ),

然后我们可以在 MealViewModel里面拿到这个 filter_view_model里面的数据

meal_view_model.dart

class HYMealViewModel extends ChangeNotifier {
//  这里不初始化的就是null
  List _meals = [];

  HYFilterViewModel _filterVM;

  List get meals {
    return _meals.where((meal) {
//      做一个过滤: filterVM

    }).toList();
  }

  void updateFilters(HYFilterViewModel filterVM)  {
    _filterVM = filterVM;
  }

这样我们就可以在update将 这个filter放到这个 meal_view_model里面

          ChangeNotifierProxyProvider(
            create: (ctx) => HYMealViewModel(),
            update: (ctx, filterVM, mealVM) {
              mealVM.updateFilters(filterVM);
            },
          ),

但是这里报了 一个警告因为这里要返回一个R

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aBEob1nA-1589850162165)(361E80315C234D1C8234B9CE1C09CAAA)]

我们这里是没有改的 所以我们这里 返回这个对象

          ChangeNotifierProxyProvider(
            create: (ctx) => HYMealViewModel(),
            update: (ctx, filterVM, mealVM) {
              mealVM.updateFilters(filterVM);
              return mealVM;
            },
          ),

注意这个ChangeNotifierProxyProvider必须卸载HYFilterViewModel导入Provider 地方的后面

void main() {
//  Provider -> ViewModel / Provider / Consumer(Selector)
  runApp(
      MultiProvider(
        providers: [
          ChangeNotifierProvider(
            create: (ctx) => HYFilterViewModel(),
          ),
          ChangeNotifierProxyProvider(
            create: (ctx) => HYMealViewModel(),
            update: (ctx, filterVM, mealVM) {
              mealVM.updateFilters(filterVM);
              return mealVM;
            },
          ),
          ChangeNotifierProvider(
            create: (ctx) => HYFavorViewModel(),
          ),
        ],
        child: MyApp(),
      )
  );
}

如果你改了当然就返回另外一个对象

我们这里就可以在meal_view_model里面做一个过滤了

  List get meals {
    return _meals.where((meal) {
//      做一个过滤: filterVM
//    我选了但是 食品没有这个属性  这个时候这个meal是不需要的
      if( _filterVM.isGlutenFree && !meal.isGlutenFree ) return false;
    }).toList();
  }
  

我选了但是 食品没有这个属性 这个时候这个meal是不需要的 我们返回一个false就可以了

这个where函数和 其它地方的filter函数是一样的

import 'package:flutter/cupertino.dart';
import 'package:project03/core/model/meal_model.dart';
import 'package:project03/core/services/meal_request.dart';
import 'package:project03/core/viewmodel/filter_view_model.dart';

class HYMealViewModel extends ChangeNotifier {
//  这里不初始化的就是null
  List _meals = [];

  HYFilterViewModel _filterVM;

  List get meals {
    return _meals.where((meal) {
//      做一个过滤: filterVM
//    我选了但是 食品没有这个属性  这个时候这个meal是不需要的

      if( _filterVM.isGlutenFree && !meal.isGlutenFree ) return false;
      if( _filterVM.isVegetarian && !meal.isVegetarian ) return false;
      if( _filterVM.isVegan && !meal.isVegan ) return false;
      if( _filterVM.isLactoseFree && !meal.isLactoseFree ) return false;
      return true;
    }).toList();
  }

  void updateFilters(HYFilterViewModel filterVM)  {
    _filterVM = filterVM;
  }

//  一旦你创建出对象 你就发送网络请求
  HYMealViewModel() {
    HYMealRequest.getMealData().then((res) {
      _meals = res;
      notifyListeners();
    });
  }
}

这样整个东西它就已经过滤了

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Xgt1rQWy-1589850162166)(42C4A2B5394D4267ACC665270F26CF4F)]

但是这里就会有一个bug了 我们如果在过滤之前收藏了一个 食物 然后过滤以后它还是会出现在收藏里面

所以我们的收藏也需要依赖这个东西

我们就要在对main进行一个修改

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2sn7ozRr-1589850162167)(701255D61B1F4D05A1E2529D85007D65)]

我们要将这个Favor这个东西也依赖这个Filter

同样要对favor进行进行改变

我们来到meal_view_model.dart 拷贝对应的代码

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-K9oJ6OWX-1589850162168)(E18450589F8D4DD3AA930E9E0D3DBF38)]

然后同样要对返回的东西 做一个改变 把这个东西复制到get里面

      if( _filterVM.isGlutenFree && !meal.isGlutenFree ) return false;
      if( _filterVM.isVegetarian && !meal.isVegetarian ) return false;
      if( _filterVM.isVegan && !meal.isVegan ) return false;
      if( _filterVM.isLactoseFree && !meal.isLactoseFree ) return false;
      return true;

favor_view_model.dart

import 'package:flutter/cupertino.dart';
import 'package:project03/core/model/meal_model.dart';
import 'package:project03/core/viewmodel/filter_view_model.dart';

class HYFavorViewModel extends ChangeNotifier {
  List _favorMeals = [];

  HYFilterViewModel _filterVM;

  void updateFilters(HYFilterViewModel filterVM)  {
    _filterVM = filterVM;
  }

  List get favorMeals {
    return _favorMeals.where((meal) {
      if( _filterVM.isGlutenFree && !meal.isGlutenFree ) return false;
      if( _filterVM.isVegetarian && !meal.isVegetarian ) return false;
      if( _filterVM.isVegan && !meal.isVegan ) return false;
      if( _filterVM.isLactoseFree && !meal.isLactoseFree ) return false;
      return true;
    }).toList();
  }

  void addMeal(HYMealModel meal) {
    _favorMeals.add(meal);
//    然后这里是要做一个通知的
    notifyListeners();
  }

  void removeMeal(HYMealModel meal) {
    _favorMeals.remove(meal);
    notifyListeners();
  }

  bool isFavor(HYMealModel meal) {
    return _favorMeals.contains(meal);
  }

  void handleMeal(HYMealModel meal) {
    if (isFavor(meal)) {
      removeMeal(meal);
    } else {
      addMeal(meal);
    }
  }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dbniF9Dz-1589850162170)(54A701B06B7F4A1D8A0FB53F5C6053A4)]

我们就发现这个代码 很多都是拷贝的 既然是拷贝的 那我们的代码就因该存在很多的重复代码

所以我们就最好对这个代码进行一个抽取

如果是两个类中存在一个重复的代码 我们就只需要给他整一个基类就可以了

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9A1fv4k6-1589850162171)(FCAD494BCD1C4B3ABAF2B5F3A0DAE1E8)]

创建一个对应的文件

但是我们在抽取的时候就发现 如果我们把整个的剪掉的话就发现 如果我们提取这些还不够

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xC5YpjJe-1589850162172)(23E01B6668A74F5190BB996BCB07D664)]

我们就需要提取 meal这种东西 favorMeals这种东西 但是这些都是名称的东西 所以我们是没有必要的

我们直接把这个剪掉

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WxX8ls7z-1589850162174)(9D5FECAAEAF340C398B39FC8F03C0DB6)]

然后把它拷过来

base_view_model.dart

import 'package:flutter/material.dart';
import 'package:project03/core/model/meal_model.dart';

import 'filter_view_model.dart';

class BaseMealViewModel extends ChangeNotifier {
//  这里不初始化的就是null
  List _meals = [];

  HYFilterViewModel _filterVM;

  void updateFilters(HYFilterViewModel filterVM)  {
    _filterVM = filterVM;
  }

  List get meals {
    return _meals.where((meal) {
//      做一个过滤: filterVM
//    我选了但是 食品没有这个属性  这个时候这个meal是不需要的

      if( _filterVM.isGlutenFree && !meal.isGlutenFree ) return false;
      if( _filterVM.isVegetarian && !meal.isVegetarian ) return false;
      if( _filterVM.isVegan && !meal.isVegan ) return false;
      if( _filterVM.isLactoseFree && !meal.isLactoseFree ) return false;
      return true;
    }).toList();
  }
}

但是这里就有一个问题了

我们进行初始化的时候 需要给这个东西赋值

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GjVO90rv-1589850162175)(C15E500103CD48509DB7C7E5865527F4)]

我们这里的meals是下滑线 我们是没有办法直接拿到的

所以我们给它搞一个setter 这个notifyListeners也因该放到这里来

  set meals(List meal) {
    _meals = meal;
    notifyListeners();
  }

这样我们的meal_view_model就变成了

import 'package:flutter/cupertino.dart';
import 'package:project03/core/model/meal_model.dart';
import 'package:project03/core/services/meal_request.dart';
import 'package:project03/core/viewmodel/base_view_model.dart';
import 'package:project03/core/viewmodel/filter_view_model.dart';

class HYMealViewModel extends BaseMealViewModel {
//  一旦你创建出对象 你就发送网络请求
  HYMealViewModel() {
    HYMealRequest.getMealData().then((res) {
      meals = res;
    });
  }
}

同样对这个favor_view_model进行抽取

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qC1chP66-1589850162176)(9845A0986FF14D2EA91C5C40519CFA2C)]

继承了这个

但是有一个问题 就是我们可以把这些 addMeal 里面的 _favorMeals.add(meal) 直接改成meals可以吗

  • 不行

这里是不可以的 因为这个meals是过滤之后的meals它会生成一个新的meals

不是原来 meals

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fXqat3iB-1589850162177)(1F6973F421A54A75B45413A282BC1B5C)]

但是 我们这里又希望能在子类里面拿这个_meals那这里有一个方法就是不要搞下滑线了

就可以拿到了

但是如果我们搞了 _就不能在子类里面访问到了 所以我们再搞一个 getter

让子类能够访问到这个meals

import 'package:flutter/material.dart';
import 'package:project03/core/model/meal_model.dart';

import 'filter_view_model.dart';

class BaseMealViewModel extends ChangeNotifier {
//  这里不初始化的就是null
  List _meals = [];

  HYFilterViewModel _filterVM;

  void updateFilters(HYFilterViewModel filterVM)  {
    _filterVM = filterVM;
  }

  List get meals {
    return _meals.where((meal) {
//      做一个过滤: filterVM
//    我选了但是 食品没有这个属性  这个时候这个meal是不需要的

      if( _filterVM.isGlutenFree && !meal.isGlutenFree ) return false;
      if( _filterVM.isVegetarian && !meal.isVegetarian ) return false;
      if( _filterVM.isVegan && !meal.isVegan ) return false;
      if( _filterVM.isLactoseFree && !meal.isLactoseFree ) return false;
      return true;
    }).toList();
  }

  set meals(List meal) {
    _meals = meal;
    notifyListeners();
  }

  List get originMeals {
    return _meals;
  }
}

然后favor里面就简单了

import 'package:project03/core/model/meal_model.dart';
import 'package:project03/core/viewmodel/base_view_model.dart';

class HYFavorViewModel extends BaseMealViewModel {
  void addMeal(HYMealModel meal) {
    originMeals.add(meal);
//    然后这里是要做一个通知的
    notifyListeners();
  }

  void removeMeal(HYMealModel meal) {
    originMeals.remove(meal);
    notifyListeners();
  }

  bool isFavor(HYMealModel meal) {
    return originMeals.contains(meal);
  }

  void handleMeal(HYMealModel meal) {
    if (isFavor(meal)) {
      removeMeal(meal);
    } else {
      addMeal(meal);
    }
  }
}

page->favor->favor.dart

里面改一下

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NcikAVDA-1589850162179)(B7FF9CB321F04B939D4847E0C992FD67)]

然后就可以了

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7k64cb81-1589850162180)(1E8397CBDF244B3CA6C3D6D574748333)]

所以我们这里可以这样管理

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-96n2frRT-1589850162182)(BE659546E26B4BA186CEE9E1A0D1B217)]

以前这个flutter的这个还不是这么写的

它更简单 但是现在他们把这个update 给抽离出来了

缓存的话就是本地化存储 但是这里我们不讲这个东西比较简单

  • 这里我们的 项目就讲完了

你可能感兴趣的:(flutter,flutter)