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: <Widget>[
            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: <Widget>[
        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>[],
    );
  }

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

然后我们往里面放东西

但是这里是一定会报错的

  Widget buildChoiceSelect() {
    return ListView(
      children: <Widget>[
        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: <Widget>[
        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: <Widget>[
          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: <Widget>[
          ListTile(
            title: Text("五谷蛋白"),
            subtitle: Text("五谷蛋白"),
            trailing: Switch(
              value: false,
              onChanged: (value) {

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

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

  Widget buildChoiceSelect() {
    return Expanded(
      child: ListView(
        children: <Widget>[
          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<HYFilterViewModel>(
        builder: (ctx, filterVM, child) {
          return ListView(
            children: <Widget>[
              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<HYFilterViewModel, HYMealViewModel>(
            create: (ctx) => HYMealViewModel(),
            update: (ctx, filterVM, mealVM) {
              return 
            },
          ),

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

meal_view_model.dart

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

  HYFilterViewModel _filterVM;

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

    }).toList();
  }

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

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

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

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

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

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

          ChangeNotifierProxyProvider<HYFilterViewModel, HYMealViewModel>(
            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<HYFilterViewModel, HYMealViewModel>(
            create: (ctx) => HYMealViewModel(),
            update: (ctx, filterVM, mealVM) {
              mealVM.updateFilters(filterVM);
              return mealVM;
            },
          ),
          ChangeNotifierProvider(
            create: (ctx) => HYFavorViewModel(),
          ),
        ],
        child: MyApp(),
      )
  );
}

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

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

  List<HYMealModel> 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<HYMealModel> _meals = [];

  HYFilterViewModel _filterVM;

  List<HYMealModel> 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<HYMealModel> _favorMeals = [];

  HYFilterViewModel _filterVM;

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

  List<HYMealModel> 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<HYMealModel> _meals = [];

  HYFilterViewModel _filterVM;

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

  List<HYMealModel> 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<HYMealModel> 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<HYMealModel> _meals = [];

  HYFilterViewModel _filterVM;

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

  List<HYMealModel> 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<HYMealModel> meal) {
    _meals = meal;
    notifyListeners();
  }

  List<HYMealModel> 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 给抽离出来了

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

  • 这里我们的 项目就讲完了
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Flutter是一种UI工具包,可以用来开发高性能、高保真度的应用程序。随着Flutter的日益普及,越来越多的开发者开始使用Flutter开发应用程序。在这样的背景下,Flutter开发实战详解这本书,为众多开发人员提供了实践性非常强的经验分享和技术指导。 这本书内容丰富,从基础概念、环境设置,到实际开发应用程序都进行了详细介绍。读完本书,读者将会具有: 1. Flutter跨平台应用开发的核心技能:包括组件库、布局、状态管理、动画、网络、数据储存等核心技能。这些技能对于任何一个Flutter开发者都是必备的。 2. 实际项目开发经验:本书包含大量搭建实际应用程序的案例分析和实例演练。通过学习这些案例,读者将会了解如何解决实际开发应用程序中所遇到的各种问题,如调试、性能优化、部署等等。 3. 高质量UI设计:本书通过实例讲解了在Flutter中如何实现高质量和美观的UI设计,并向读者展示了如何借鉴开源组件库并构建自己的一套用户界面组件库。 总之,Flutter开发实战详解这本书是适合所有Flutter开发者的一本经典著作。如果您想要成为一名熟练的跨平台应用程序开发者,那么这本书绝对值得阅读和好好学习。 ### 回答2: Flutter开发实战详解是一本非常实用的书,书中涵盖了Flutter的开发基础、组件使用、实战应用等方面的知识。对于想要学习Flutter的开发者来说,这本书具有非常实用的指导意义。在教学方面,本书使用了简单易懂、详细生动的语言,配合大量的实战案例和代码示例,让读者能够更加深入地理解Flutter的开发方式和设计模式,掌握如何使用Flutter开发应用程序。书中还介绍了如何创建一个Flutter项目,如何使用Flutter中的UI组件和动画,以及如何使用Flutter中的第三方库来扩展应用功能等内容,让读者能够通过实践快速掌握Flutter的开发技能。总之,Flutter开发实战详解是一本非常优秀的Flutter开发进阶书籍,主要针对于那些已经掌握了基础知识,并想进一步深入学习Flutter开发实践的开发者,不论是初学者还是进阶者,都值得一读。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值