Flutter学习-项目实战

首先我们可以使用flutter create xxx指令在终端创建项目, 也可以使用工具来创建一个新的 项目, 我这里是在终端直接创建的,以防使用IDE创建又自动配置一些内容

1 项目的配置

我们创建完项目之后,这里我们需要配置一些基本的信息(Flutter移动端-> App)
我们准备对以下内容配置:

  • appid: 程序唯一id
  • 应用名称
  • icon: App的icon图标
  • lanuncher: App的启动图

由于Android和IOS端的配置是不一样的,这里我们需要分开配置

1.1 Android的基本配置

由于我们是使用Android studio工具来开发的,所以对于Android的配置我们可以直接在工具中配置

  • 配置Appid:
    在这里插入图片描述

  • 应用名称、icon图标
    在这里插入图片描述

在这里插入图片描述

  • 启动图
    Android中默认的启动图是一片空白的,这是Flutter的默认设置效果。
    根据手机的分辨率不同去加载不同分辨率的图片,如果该分辨率下没有图片则会去高分辨率的文件中找
    在这里插入图片描述

初始文件如上图, 是直接启动是白色,我们如果需要修改启动图可以进行如下修改
在这里插入图片描述

  • 注意: 由于本人是一名IOS开发,所以这里关于IOS端的基本信息配置,我这里就不做介绍了, 如果其他人需要

IOS端配置:

  1. 项目的目录结构划分
  2. 主题相关
  3. 在这里插入图片描述
    6.路由配置

在这里插入图片描述

2. 项目注意事项点

2.1 字符串颜色转换成颜色

//1.将得到的颜色字符串,转换成16进制的数字
    if (color != null) {
      /**
       * int.Parse()抛出了异常,原因是int.Parse()是一种类容转换;表示将数字内容的字符串转为int类型。
       * 如果字符串为空,则抛出ArgumentNullException异常
       * 如果字符串内容不是数字,则抛出FormatException异常;
       * 如果字符串内容所表示数字超出int类型可表示的范围,则抛出OverflowException异常
       */
      /**
       *  int中有一个parse方法来解析字符串 会抛出FormatException异常 radix:默认基数10进制,我们需要指定是16进制
       */
      final colorInt = int.parse(color!, radix: 16);
      //把透明度直接通过 或  的方式加进去 ,然后调用Color的构造方法得到一个有透明度的颜色
      backgroundColor = Color(colorInt | 0xFF000000);
    }

2.2 使用FutureBuilder在某些情况下代理StatefulWidgets

我们首页展示这美食分类界面:在这里插入图片描述

我们发现首页其实是一直存在的,这个时候我们使用StatefulWidgets来实现需要再initState方法中加载网络数据, 在使用setState方法来更新界面

这里我们完全可以使用FutureBuilder来代替,因为首页不需要频繁的更新

2.2.1 StatefulWidget实现

class GYHomeContent extends StatefulWidget {
  const GYHomeContent({Key? key}) : super(key: key);

  @override
  _GYHomeContentState createState() => _GYHomeContentState();
}

class _GYHomeContentState extends State<GYHomeContent> {
  List<GYCategoryModel> items = [];
  @override
  void initState() {
    // TODO: implement initState
    super.initState();

    //加载网络数据,这里是加载本地json文件数据
    GYJsonParse.getCategryData().then((value) {
      //加载数据完成之后,刷新数据显示
      setState(() {
        items = value;
      });
    });
  }


  @override
  Widget build(BuildContext context) {
    return GridView.builder(
        padding: EdgeInsets.all(20.px),
        itemCount: items.length,
        gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
            crossAxisCount: 2, //交叉轴方向显示2个item
            childAspectRatio: 1.5, //宽高比
            crossAxisSpacing: 20.px,//交叉轴item之间的距离
            mainAxisSpacing: 20.px//主轴方向item之间的距离
        ),
        itemBuilder: (context, index) {
          final bgColor = items[index].backgroundColor;
          return Container(
            decoration: BoxDecoration(
              color: Colors.white,
              borderRadius: BorderRadius.circular(12),
              //设置item的颜色为渐变色
              gradient: LinearGradient(
                colors: [
                  bgColor.withOpacity(.5),
                  bgColor
                ]
              )
            ),
            alignment: Alignment.center,
            child: Text(items[index].title ?? "", style: Theme.of(context).textTheme.headline3?.copyWith(
              fontWeight: FontWeight.bold
            ),),
          );
        });
  }
}

2.2 FutureBuilder实现

class GYHomeContent extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    return FutureBuilder<List<GYCategoryModel>>(
        future: GYJsonParse.getCategryData(),
        /**
         * 在某些情况下FutureBuilder这个widget可以代替StatefulWidget来使用,使用代码看起来更加简洁
         * 但是该widget有局限性:
         * 1、如果该页面需要经常刷新数据,可能需要缓存数据, 那么该widget就无法满足, 如果该页面多次刷新,那么使用FutureBuilder会造成多次发送请求
         * 2、如果该页面需要实现 上拉加载功能,  该widget也无法完成
         */

        builder: (context, snapshotData) {
//hasData: 表示,如果请求加载的数据回来了,返回true ,否则返回false
          if (!snapshotData.hasData)
            return Center(
              child: CircularProgressIndicator(),
            );
//如果请求错误,则显示错误页面
          if (snapshotData.error != null) return Center(child: Text("请求失败"));
          final items = snapshotData.data;

          return GridView.builder(
              padding: EdgeInsets.all(20.px),
              itemCount: items!.length,
              gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
                  crossAxisCount: 2, //交叉轴方向显示2个item
                  childAspectRatio: 1.5, //宽高比
                  crossAxisSpacing: 20.px, //交叉轴item之间的距离
                  mainAxisSpacing: 20.px //主轴方向item之间的距离
                  ),
              itemBuilder: (context, index) {
                return GYHomeCategoryItem(items[index]);
              });
        });
  }
}

两者实现的效果是一样的,但是在这种情况下,使用FutureBuilder实现起来,代码可能看起来简介

2.3 实现路由跳转,并传值

  • 配置路由
    在这里插入图片描述

  • 路由跳转
    在这里插入图片描述

  • 取出路由传递的值
    在这里插入图片描述
    注意:拿到的都是栈顶widget的元素,所以不管是在栈的那个页面拿数据都是一样的

2.4 使用Provider实现数据共享

  • 创建共享数据
class GYMealViewModel extends ChangeNotifier {
  /*需要共享的数据*/
  List<GYMealModel> _meals = [];

  /*提供get方法*/
  List<GYMealModel> get meals {
    return _meals;
  }

  //在初始化方法中获取数据
  GYMealViewModel() {
    //获取菜谱详细数据,本来是网络请求获取数据,目前是获取本地json文件的数据
    GYJsonParse.getMealData().then((value) {
      _meals = value;
      //发送通知告诉 使用共享数据的地方,共享数据更新了
      notifyListeners();
    });
  }
}
  • 配置共享数据
void main() {
   Provider -> ViewModel/Provider/Consumer(Selector)
  runApp(
    //使用ChangeNotifierProvider作为顶层,不管在那里都可以访问这个共享数据
    ChangeNotifierProvider(
      //这里不会再启动App的时候就加载数据的, 是在第一次使用到共享数据的时候才会加载,是一个懒加载
      create: (ctx) => GYMealViewModel(),
      child: MyApp(),
    )
  );
}
  • 使用共享数据,Consumer和Selector两种方式
// 使用Consumer的方式来实现  共享数据的展示和获取
   @override
   Widget build(BuildContext context) {
     return Consumer<GYMealViewModel>(
       /**
        * 第一个参数: context 上下文
        * 第二个参数: ChangeNotifier对应的实例,共享的数据,也是我们在build函数中使用的主要对象
        * 第三个参数: child:目的是进行优化把不希望重新build的子widget层放到child属性之后,这样更新数据之后就不会重新build整个widget树
        */
         builder: (context, mealViewModel, child) {
             //根据id筛选出数据
           final meals = mealViewModel.meals.where((element) => element.categories!.contains(_categoryModel.id)).toList();
           return ListView.builder(itemBuilder: (context, index,) {
             return Text(meals[index].title ?? "名称为空");
           }, itemCount: meals.length,);
         }
     );
   }


// 上面使用Consumer的方式来实现共享数据的获取, 这里其实使用Selector的方式来实现更加的简介方便
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Selector<GYMealViewModel, List<GYMealModel>>(
      builder: (context, meals, child) {
        return ListView.builder(
          itemBuilder: (
            context,
            index,
          ) {
            return Text(meals[index].title ?? "名称为空");
          },
          itemCount: meals.length,
        );
      },
      //这里是把 A 转换成 S
      selector: (context, mealVM) {
        return mealVM.meals
            .where((element) => element.categories!.contains(_categoryModel.id))
            .toList();
      },
      shouldRebuild: (pre, next) {
        //比较两个数组是否完全一样 ,一样则不重新build, 如果不一样则重新build
        return !ListEquality().equals(pre, next);
      },
    );
  }

2.5 item详情页面实现

在这里插入图片描述

2.5.1 double.infinity 属性

在这里插入图片描述

  • double.infinity : 表示widge在该方向的尽可能占据最大的宽度

2.5.2 Colum中嵌套ListView的问题,报错问题

在这里插入图片描述

运行代码会报一个flutter中经典的错误, 其实这种场景我们会比较常见
在这里插入图片描述

  • 问题原因: Colum是希望内部所有子Widget都有一个明确的高度, 而ListView在垂直方向,是希望尽可能占据最大的空间,具体占据多大空间,不知道,所以产生了冲突,然后就上述错误

  • 解决原因:

    • 我们可以直接ListView的外层的Container的高度,这样设置可以正常运行,但是可能会有一个内部滚动的效果
    • 直接设置ListView的高度,让ListView直接包裹内容高度

在这里插入图片描述

2.5.3 Colum滑动

我们都知道Colum本身不是不可滑动的,当内容的显示超过Colum的显示范围时,那么就会报错
,那我们如何使Colum边的可以滑动了

在Colum的外层添加一个SingleChildScrollViewwidget即可,该Widget就可以使Colum进行滑动
在这里插入图片描述

2.6 flutter中实现抽屉效果

2.6.1 实现抽屉效果

在Flutter中如何实现抽屉效果, 其实很简单, flutter中给我们提供了一个属性drawer来设置抽屉效果
在这里插入图片描述

2.6.2 如何控制抽屉效果的宽度

我们查看Drawerwidget的属性,并没有发现有属性直接设置宽度:
在这里插入图片描述

  • 解决办法: 如何控制抽屉的宽度, 本身是没有一个属性来控制宽度, 我们可以在外面包裹一层Container,通过设置Conatiner的宽度来达到控制抽屉效果的看度
    在这里插入图片描述

2.6.3 如何修改drawer的icon图标

我知道,在AppBar中有一个属性leading,这个可以修改左侧导航栏的图标

在这里插入图片描述

但是当我们修改导航栏左侧icon图标之后,我们发现点击按钮没有反应了,这个时候需要我们自己去打开 抽屉效果页面, 我们可以如下实现:
在这里插入图片描述

但是当我们点击按钮时,发现还是没有反应, 这是因为这句代码取的Scaffold根本不是你自己创建的那个Scaffold,因为你拿的是build方法的context,会沿着该widget树往上寻找,但是你创建的Scaffold是在这个widget的build方法中, 所以是肯定找不到的

  • 解决办法
    在这里插入图片描述

做如上修改之后, 抽屉效果页面就可以打开了

2.7 实现过滤功能

在这里插入图片描述

具体实现功能参考demo代码

  • 0
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1. 官方文档:Flutter 官方文档提供了完整的学习资源,包括入门指南、教程、API 参考、示例代码等等。官网:https://flutter.dev/docs 2. Flutter 中文网:Flutter 中文网是针对中国开发者的 Flutter 学习资源,提供了中文文档、教程、视频课程、社区论坛等。官网:https://flutterchina.club/ 3. Flutter 实战教程:Flutter 实战教程是一本由 Flutter 官方团队和社区作者合作编写的实战教程,全面讲解了 Flutter 的各个方面。官网:https://book.flutterchina.club/ 4. Flutter 官方 YouTube 频道:Flutter 官方 YouTube 频道提供了大量的教程视频,包括 Flutter 入门、布局、动画、网络请求等等。官网:https://www.youtube.com/c/FlutterDev 5. Flutter Gallery:Flutter Gallery 是一个官方提供的 Flutter 示例应用,展示了 Flutter 的各种特性和功能,可以帮助开发者更好地了解 Flutter。官网:https://gallery.flutter.dev/ 6. Flutter App Samples:Flutter App Samples 是一个由社区维护的 Flutter 应用示例库,包括各种类型的应用,如计算器、天气应用、电影评分应用等等。官网:https://flutter.dev/docs/cookbook#samples 7. Flutter 中文社区:Flutter 中文社区是一个由开发者自发组建的社区,提供了丰富的学习资源,包括博客、论坛、教程、实战项目等等。官网:https://flutter.cn/ 8. Flutter UI 网站:Flutter UI 网站提供了大量的 Flutter UI 设计资源,包括组件、模板、样式等等,可以帮助开发者快速构建 UI。官网:https://flutterui.net/ 9. Flutter Weekly:Flutter Weekly 是一个由社区维护的每周更新的 Flutter 新闻和教程汇总,可以让开发者快速了解最新的 Flutter 动态。官网:https://flutterweekly.net/ 10. Flutter 开源项目:Flutter 开源项目是一个由社区维护的 Flutter 应用开源项目库,包括各种类型的应用,如电商应用、新闻应用、社交应用等等,可以帮助开发者学习和实践 Flutter。官网:https://github.com/Solido/awesome-flutter

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值