Flutter项目实战之女装商城------添加轮播图、分类导航、商品推荐、广告位处理、底部商品

前言:

时间飞逝,伴随着打工人春节的结束心也得跟着回收一下,新年新气象,今年也没有什么高大上的目标,暂且先把去年落下的各种课程的学习给完整结束了,不过计划还是会再多学点其它新的东东,到时看情况,年后的第一次学习从之前落下的Flutter开始。

距离上一次Flutter项目实战之女装商城------首页设计分析、数据准备、Dio请求处理、接口配置、请求首页数据 - cexo - 博客园的学习已经时隔4个月之久了,先来回忆一下当时APP的样子做到了哪了:

Flutter项目实战之女装商城------添加轮播图、分类导航、商品推荐、广告位处理、底部商品_第1张图片

其实要注意,后台数据是用的node来搭建的,注意本地先npm start一下:

Flutter项目实战之女装商城------添加轮播图、分类导航、商品推荐、广告位处理、底部商品_第2张图片

然后本机ip会随时变动,记得根据本机当前的ip,将代码修改一下,比如:

Flutter项目实战之女装商城------添加轮播图、分类导航、商品推荐、广告位处理、底部商品_第3张图片

基本也就搭了个架子,接下来则从首页开始继续往下撸。

添加轮播图:

效果:

接下来先来搭建一下首页的商品轮播图,样子如下:

Flutter项目实战之女装商城------添加轮播图、分类导航、商品推荐、广告位处理、底部商品_第4张图片

添加依赖:

要实现这样的效果,当然得借助于三方库喽,这里使用flutter_swiper,官方地址:flutter_swiper | Flutter Package,

Flutter项目实战之女装商城------添加轮播图、分类导航、商品推荐、广告位处理、底部商品_第5张图片

先来在工程中添加依赖:

Flutter项目实战之女装商城------添加轮播图、分类导航、商品推荐、广告位处理、底部商品_第6张图片

构建轮播组件:

1、导包:

Flutter项目实战之女装商城------添加轮播图、分类导航、商品推荐、广告位处理、底部商品_第7张图片

先来对home_page的代码进行一个全局回顾,目前长这样子:

import 'dart:convert';

import 'package:flutter/material.dart';
import 'package:flutter_easyrefresh/easy_refresh.dart';
import 'package:fluttershop/config/color.dart';
import 'package:fluttershop/config/string.dart';
import 'package:fluttershop/service/http_service.dart';
import 'package:flutter_swiper/flutter_swiper.dart';

class HomePage extends StatefulWidget {
  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State
    with AutomaticKeepAliveClientMixin {
  //这里只是做一个演示,其实是不用加这个的,因为咱们的BottomNavigationBar是由IndexedStack构建的,具体参考index_page.dart文件
  @override
  bool get wantKeepAlive => true;
  GlobalKey _footerKey =
      GlobalKey();

  @override
  void initState() {
    super.initState();
    print('home_page.initState()');
  }

  @override
  Widget build(BuildContext context) {
    print('home_page.build()');
    return Scaffold(
      backgroundColor: Color.fromRGBO(244, 245, 245, 1.0),
      appBar: AppBar(
        title: Text(KString.homeTitle),
        centerTitle: true,
      ),
      body: FutureBuilder(
          future: request('homePageContext', formData: null),
          builder: (context, snapshot) {
            if (snapshot.hasData) {
              var data = json.decode(snapshot.data.toString());
              print("gethomePageContext data:${data}");
              var dataList = data['data'];
              List swiperDataList =
                  (dataList['slides'] as List).cast(); //轮播图
              List navigatorList =
                  (dataList['category'] as List).cast(); //分类
              List recommendList =
                  (dataList['recommend'] as List).cast(); //商品推荐
              List floor1 = (dataList['floor1'] as List).cast(); //底部商品推荐
              Map fp1 = dataList['floor1Pic']; //广告
              return EasyRefresh.custom(
                enableControlFinishRefresh: false,
                enableControlFinishLoad: true,
                footer: ClassicalFooter(
                  key: _footerKey,
                  bgColor: Colors.white,
                  textColor: KColor.refreshTextColor,
                  infoColor: KColor.refreshTextColor,
                  noMoreText: '',
                  //加载中...
                  loadingText: KString.loading,
                  loadReadyText: KString.loadReadyText,
                ),
                slivers: [
                  SliverList(
                    delegate: SliverChildBuilderDelegate(
                      (context, index) {
                        return Container(
                          width: 60.0,
                          height: 60.0,
                          child: Center(
                            child: Text('$index'),
                          ),
                          color: index % 2 == 0
                              ? Colors.grey[300]
                              : Colors.transparent,
                        );
                      },
                      childCount: 5,
                    ),
                  ),
                ],
                onLoad: () async {
                  print('开始加载更多');
                  //TODO 执行下一页的请求
                },
              );
            } else {
              return Center(
                child: Text('加载中...'),
              );
            }
          }),
    );
  }
}

此时需要用ListView来构建整个界面的内容,所以代码需要修改一下:

Flutter项目实战之女装商城------添加轮播图、分类导航、商品推荐、广告位处理、底部商品_第8张图片

2、编写Swiper组件框架:

Flutter项目实战之女装商城------添加轮播图、分类导航、商品推荐、广告位处理、底部商品_第9张图片

其中注意Dart的语法,我在编写这块时都感觉完全生疏了,时间一长工作中又不用铁定健忘,这时之前学习留下的笔记就发挥它的威力了,这里说的Dart语法就是构造函数相关,一个是:

关于它可以回忆Fluter基础巩固之Dart语言详解<二> - cexo - 博客园,

Flutter项目实战之女装商城------添加轮播图、分类导航、商品推荐、广告位处理、底部商品_第10张图片

另一个是在它定义构造时用了一个大括号:

Flutter项目实战之女装商城------添加轮播图、分类导航、商品推荐、广告位处理、底部商品_第11张图片

这又是啥语法呢?可选命名参数,如果忘了可以参考Fluter基础巩固之Dart语言详解<一> - cexo - 博客园,如下:

Flutter项目实战之女装商城------添加轮播图、分类导航、商品推荐、广告位处理、底部商品_第12张图片

3、具体编写Swiper组件:

接下来具体编写Swpier组件的构建,先来参考官方的DEMO:

Flutter项目实战之女装商城------添加轮播图、分类导航、商品推荐、广告位处理、底部商品_第13张图片

这里就一气呵成了,只对关键地方做下说明:

Flutter项目实战之女装商城------添加轮播图、分类导航、商品推荐、广告位处理、底部商品_第14张图片

其中关于fit属性有很多种,这里参考大佬的Flutter中 BoxFit 的几种样式说明,贴出来为我所用:

BoxFit.contain

将会尽可能的伸展来达到组件的边缘。

Flutter中 BoxFit 的几种样式

BoxFit.cover

将会尽可能小的放大来铺满整个组件

Flutter中 BoxFit 的几种样式

BoxFit.fill

通过拉伸(改变原图比例)来铺满组件

Flutter中 BoxFit 的几种样式

BoxFit.fitHeight

保持原图比例,拉升高度来达到组件的高度

Flutter中 BoxFit 的几种样式

BoxFit.fitWidth

保持原图比例,拉升宽度来达到组件的宽度

Flutter中 BoxFit 的几种样式

BoxFit.none

正常展示,默认居中。

Flutter中 BoxFit 的几种样式

BoxFit.scaleDown

保持在组件中并且不改变比例

Flutter中 BoxFit 的几种样式

另外还有一个关于设置尺寸的代码:

Flutter项目实战之女装商城------添加轮播图、分类导航、商品推荐、广告位处理、底部商品_第15张图片

这块是屏幕适配的写法,记得不要直接写死一个值,关于这块可以回忆博文阅读密码验证 - 博客园,不过多解释。

然后需要设置里面小红点和自动轮播,很简单:

Flutter项目实战之女装商城------添加轮播图、分类导航、商品推荐、广告位处理、底部商品_第16张图片

4、运行:

Flutter项目实战之女装商城------添加轮播图、分类导航、商品推荐、广告位处理、底部商品_第17张图片

呃,咋图片没加载出来空白了呢?这里要注意了【以后这块就不强调了】!!!然后看一下每个图片的url是这么个形式:

Flutter项目实战之女装商城------添加轮播图、分类导航、商品推荐、广告位处理、底部商品_第18张图片

因为node服务是运行在电脑上的,127.0.0.1代表的是电脑的本机ip,但是!!!我运行是在真机上,而非电脑上开设的模拟器,那很显然在手机上是无法通过127.0.0.1这个电脑端的本地地址来访问图片了,解决办法也很简单,直接用电脑的局域网ip既可,查看一下电脑的ip:

Flutter项目实战之女装商城------添加轮播图、分类导航、商品推荐、广告位处理、底部商品_第19张图片

然后看一下目前咱们node的baseurl的ip是:

Flutter项目实战之女装商城------添加轮播图、分类导航、商品推荐、广告位处理、底部商品_第20张图片

Flutter项目实战之女装商城------添加轮播图、分类导航、商品推荐、广告位处理、底部商品_第21张图片

当然这个ip可能随着网络的变化会不断变化,所以以后在学习之前一定先要确保ip地址是正确的,再重启一下npm,然后再运行就ok了:

Flutter项目实战之女装商城------添加轮播图、分类导航、商品推荐、广告位处理、底部商品_第22张图片

效果如下:

嗯,不错~~整体也比较简单。

分类导航:

效果:

Flutter项目实战之女装商城------添加轮播图、分类导航、商品推荐、广告位处理、底部商品_第23张图片

其中图标都是后台返的,另外最多就支持10个分类,如果超过10个则需要截取。

具体实现:

1、列表中增加一个widget:

Flutter项目实战之女装商城------添加轮播图、分类导航、商品推荐、广告位处理、底部商品_第24张图片

2、使用GridView来构建:

对于分类这种网络布局可以使用Flutter自带的GridView来进行构建,关于它的用法可以参考开启Fluter基础之旅<二>-------Future再论、常用组件、Material Design风格组件学习 - cexo - 博客园,下面来构建一下:

Flutter项目实战之女装商城------添加轮播图、分类导航、商品推荐、广告位处理、底部商品_第25张图片

这块比较简单,其中标红的在设置大小时都用到了EdgeInsets这个类,这个在之前的学习中也大量用到了,看一下这个类的官方解释:

Flutter项目实战之女装商城------添加轮播图、分类导航、商品推荐、广告位处理、底部商品_第26张图片

总之就是对于方便给一个元素设置左右上下的间隙,而它比较经典的方法有Flutter之EdgeInsets_u013095264的博客-CSDN博客:

Flutter项目实战之女装商城------添加轮播图、分类导航、商品推荐、广告位处理、底部商品_第27张图片

关于这块要记得熟练运用,另外对于GridView的这个属性也稍加说明一下:

Flutter项目实战之女装商城------添加轮播图、分类导航、商品推荐、广告位处理、底部商品_第28张图片

其中physics是Flutter中的动画的一个种类,回忆一下开启Fluter基础之旅<四>-------表格、动画、手势 - cexo - 博客园:

Flutter项目实战之女装商城------添加轮播图、分类导航、商品推荐、广告位处理、底部商品_第29张图片

而常见的physics有很多,看一下它的子类便可以了解:

Flutter项目实战之女装商城------添加轮播图、分类导航、商品推荐、广告位处理、底部商品_第30张图片

这块就点一下,可以在之后慢慢了解。

接下来则来设置一下GridView的item项:

Flutter项目实战之女装商城------添加轮播图、分类导航、商品推荐、广告位处理、底部商品_第31张图片

那如何构建呢?如下:

Flutter项目实战之女装商城------添加轮播图、分类导航、商品推荐、广告位处理、底部商品_第32张图片

接下来就来构建一下列表项,由于它也是需要可以进行点击的,所以依然用InkWell,比较简单,贴出来:

Widget _gridViewItemUI(BuildContext context, item, index) {
    return InkWell(
      onTap: () {
        //TODO 商品分类点击进详情
      },
      child: Column(
        children: [
          Image.network(
            item['image'],
            width: ScreenUtil().setWidth(95),
          ),
          Text(item['firstCategoryName']),
        ],
      ),
    );
  }

接下来运行一下,看有木有问题,发现报错了。。

看IDE报错的详情:

Flutter项目实战之女装商城------添加轮播图、分类导航、商品推荐、广告位处理、底部商品_第33张图片

其实是犯了一个低级错,啥错呢?

Flutter项目实战之女装商城------添加轮播图、分类导航、商品推荐、广告位处理、底部商品_第34张图片

再运行一下:

Flutter项目实战之女装商城------添加轮播图、分类导航、商品推荐、广告位处理、底部商品_第35张图片

商品推荐:

效果:

Flutter项目实战之女装商城------添加轮播图、分类导航、商品推荐、广告位处理、底部商品_第36张图片

 当然是支持左右滑动了。

具体实现:

1、列表中增加一个widget:

Flutter项目实战之女装商城------添加轮播图、分类导航、商品推荐、广告位处理、底部商品_第37张图片

//商品推荐组件
class RecommendUI extends StatelessWidget {
  final List recommendList;

  RecommendUI({Key key, this.recommendList}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return null;
  }
}

2、准备推荐商品标题widget:

先来准备子widget,布局也就是按从局部到整体的思路一步步来进行,也就是推荐的标题,如下:

Flutter项目实战之女装商城------添加轮播图、分类导航、商品推荐、广告位处理、底部商品_第38张图片

其中涉及到几个色值和字符串,提到公共的地方为:

Flutter项目实战之女装商城------添加轮播图、分类导航、商品推荐、广告位处理、底部商品_第39张图片

Flutter项目实战之女装商城------添加轮播图、分类导航、商品推荐、广告位处理、底部商品_第40张图片

3、准备推荐商品列表widget:

Flutter项目实战之女装商城------添加轮播图、分类导航、商品推荐、广告位处理、底部商品_第41张图片

目前先简单显示一个文本,先将整个界面架子组拼起来。

4、将子widget组拼起来:

Flutter项目实战之女装商城------添加轮播图、分类导航、商品推荐、广告位处理、底部商品_第42张图片

接下来整体看一下基本架子的样子:

Flutter项目实战之女装商城------添加轮播图、分类导航、商品推荐、广告位处理、底部商品_第43张图片

5、准备列表item的widget:

Flutter项目实战之女装商城------添加轮播图、分类导航、商品推荐、广告位处理、底部商品_第44张图片

//列表item
  Widget _item(context, index) {
    return InkWell(
      onTap: () {
        //TODO 商品点击进详情
      },
      child: Container(
        width: ScreenUtil().setWidth(200),
        padding: EdgeInsets.all(0.0),
        decoration: BoxDecoration(
            color: Colors.white,
            border: Border(
              left: BorderSide(width: 0.5, color: KColor.defaultBorderColor),
            )),
        child: Column(
          children: [
            Image.network(recommendList[index]['image']),
            //打折价格
            Text(
              '¥${recommendList[index]['presentPrice']}',
              style: TextStyle(color: KColor.presentPriceTextColor),
            ),
            //原价
            Text(
              '¥${recommendList[index]['oriPrice']}',
              style: KFont.oriPriceStyle,
            ),
          ],
        ),
      ),
    );

其中配置相关的属性如下:

Flutter项目实战之女装商城------添加轮播图、分类导航、商品推荐、广告位处理、底部商品_第45张图片

其中对于有中划线的原价以后在多处页面都可能会用到,所以这里对这样的样式字体进行一个抽取:

Flutter项目实战之女装商城------添加轮播图、分类导航、商品推荐、广告位处理、底部商品_第46张图片

Flutter项目实战之女装商城------添加轮播图、分类导航、商品推荐、广告位处理、底部商品_第47张图片

接下来运行看一下效果:

细节调整:

细节一:防止溢出处理

看这张图:

Flutter项目实战之女装商城------添加轮播图、分类导航、商品推荐、广告位处理、底部商品_第48张图片

其实这里加一个防溢出的代码既可,如下:

Flutter项目实战之女装商城------添加轮播图、分类导航、商品推荐、广告位处理、底部商品_第49张图片

再运行:

Flutter项目实战之女装商城------添加轮播图、分类导航、商品推荐、广告位处理、底部商品_第50张图片

看一下它的作用:

Flutter项目实战之女装商城------添加轮播图、分类导航、商品推荐、广告位处理、底部商品_第51张图片

细节二:图片压缩调整

目前对于这张图很不和谐:

Flutter项目实战之女装商城------添加轮播图、分类导航、商品推荐、广告位处理、底部商品_第52张图片

此时就需要给图片增加压缩属性:

Flutter项目实战之女装商城------添加轮播图、分类导航、商品推荐、广告位处理、底部商品_第53张图片

再看效果:

貌似上下木有间隙,看着有点别扭,因为有处笔误写错了:

Flutter项目实战之女装商城------添加轮播图、分类导航、商品推荐、广告位处理、底部商品_第54张图片

修改一下:

Flutter项目实战之女装商城------添加轮播图、分类导航、商品推荐、广告位处理、底部商品_第55张图片

再来看一下:

Flutter项目实战之女装商城------添加轮播图、分类导航、商品推荐、广告位处理、底部商品_第56张图片

关于Image的几种压缩效果自己一个个试就成,不需要记,做得多了也就知道其它的意思了,就类似于Android的ImageView控件一样,它的缩放方式也很多,实际中还得根据产品的需求自己去尝试调试,具体这块的属性可以参考:Flutter中Image的fit属性解析 - 简书。

广告位处理:

效果:

Flutter项目实战之女装商城------添加轮播图、分类导航、商品推荐、广告位处理、底部商品_第57张图片

就是一张纯图,没啥逻辑。

具体实现: 

1、准备商品广告widget:

这块比较简单,直接贴出:

Flutter项目实战之女装商城------添加轮播图、分类导航、商品推荐、广告位处理、底部商品_第58张图片

//商品推荐中间广告
class FloorPic extends StatelessWidget {
  final Map floorPic;

  FloorPic({Key key, this.floorPic}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
        margin: EdgeInsets.only(top: 10.0),
        child: InkWell(
          onTap: () {
            //TODO 商品点击进详情
          },
          child: Image.network(
            floorPic['PICTURE_ADDRESS'],
            fit: BoxFit.cover,
          ),
        ));
  }
}

2、运行:

Flutter项目实战之女装商城------添加轮播图、分类导航、商品推荐、广告位处理、底部商品_第59张图片

底部商品:

效果:

Flutter项目实战之女装商城------添加轮播图、分类导航、商品推荐、广告位处理、底部商品_第60张图片

貌似这布局有点复杂,其实这里是涉及到row和column的嵌套布局,也是实际项目中会经常用到的一种布局方式,通过这个模块的编写正好可以掌握这种布局方法。

具体实现:

1、列表中增加一个widget:

Flutter项目实战之女装商城------添加轮播图、分类导航、商品推荐、广告位处理、底部商品_第61张图片

//商品推荐下层
class Floor extends StatelessWidget {
  final List floor;

  Floor({Key key, this.floor}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container();
  }
}

2、使用Row来搭建左右两列效果:

Flutter项目实战之女装商城------添加轮播图、分类导航、商品推荐、广告位处理、底部商品_第62张图片

3、具体构建五大子widget:

先来构建左边上面大图:

Flutter项目实战之女装商城------添加轮播图、分类导航、商品推荐、广告位处理、底部商品_第63张图片

比较简单,接下来四个就依葫芦画瓢了,当然间隙需要稍加修改一下,一口气呵成:

//商品推荐下层
class Floor extends StatelessWidget {
  final List floor;

  Floor({Key key, this.floor}) : super(key: key);

  //跳转到商品详情
  void jumpDetail(context, String goodId) {
    //TODO
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      child: Row(
        children: [
          //左侧商品
          Expanded(
            child: Column(
              children: [
                //左上图
                Container(
                  padding: EdgeInsets.only(top: 4),
                  height: ScreenUtil().setHeight(400),
                  child: InkWell(
                      onTap: () {
                        jumpDetail(context, floor[0]['goodsId']);
                      },
                      child: Image.network(
                        floor[0]['image'],
                        fit: BoxFit.cover,
                      )),
                ),
                //左下图
                Container(
                  padding: EdgeInsets.only(top: 1, right: 1),
                  height: ScreenUtil().setHeight(200),
                  child: InkWell(
                      onTap: () {
                        jumpDetail(context, floor[1]['goodsId']);
                      },
                      child: Image.network(
                        floor[1]['image'],
                        fit: BoxFit.cover,
                      )),
                ),
              ],
            ),
          ),
          //右侧商品
          Expanded(
            child: Column(
              children: [
                //右上图
                Container(
                  padding: EdgeInsets.only(top: 4, left: 1, bottom: 1),
                  height: ScreenUtil().setHeight(200),
                  child: InkWell(
                      onTap: () {
                        jumpDetail(context, floor[2]['goodsId']);
                      },
                      child: Image.network(
                        floor[2]['image'],
                        fit: BoxFit.cover,
                      )),
                ),
                //右中图
                Container(
                  padding: EdgeInsets.only(top: 1, left: 1),
                  height: ScreenUtil().setHeight(200),
                  child: InkWell(
                      onTap: () {
                        jumpDetail(context, floor[3]['goodsId']);
                      },
                      child: Image.network(
                        floor[3]['image'],
                        fit: BoxFit.cover,
                      )),
                ),
                //右下图
                Container(
                  padding: EdgeInsets.only(top: 1, left: 1),
                  height: ScreenUtil().setHeight(200),
                  child: InkWell(
                      onTap: () {
                        jumpDetail(context, floor[4]['goodsId']);
                      },
                      child: Image.network(
                        floor[4]['image'],
                        fit: BoxFit.cover,
                      )),
                ),
              ],
            ),
          ),
        ],
      ),
    );
  }
}

4、给Row设置属性:

最后还需要给Row设置两个属性,如下:

mainAxisAlignment:

Flutter项目实战之女装商城------添加轮播图、分类导航、商品推荐、广告位处理、底部商品_第64张图片

其中它有很多选项:

Flutter项目实战之女装商城------添加轮播图、分类导航、商品推荐、广告位处理、底部商品_第65张图片

其中只需要对spaceBetween、spaceAround、spaceEvenly进行了解既可,具体啥意思可以参考这位大佬的Flutter学习之MainAxisAlignment属性详解_bkysdc的博客-CSDN博客,介绍还是蛮详细的,其中盗个图:

Flutter项目实战之女装商城------添加轮播图、分类导航、商品推荐、广告位处理、底部商品_第66张图片

三者文字简述如下:

  • spaceBetween:

    Flutter项目实战之女装商城------添加轮播图、分类导航、商品推荐、广告位处理、底部商品_第67张图片

  • spaceAround:

    Flutter项目实战之女装商城------添加轮播图、分类导航、商品推荐、广告位处理、底部商品_第68张图片

  • spaceEvenly:

    Flutter项目实战之女装商城------添加轮播图、分类导航、商品推荐、广告位处理、底部商品_第69张图片

对于这些属性的理解还得在以后项目中的使用中来慢慢消化。

mainAxisSize:

Flutter项目实战之女装商城------添加轮播图、分类导航、商品推荐、广告位处理、底部商品_第70张图片

其中它也有两个选项:

Flutter项目实战之女装商城------添加轮播图、分类导航、商品推荐、广告位处理、底部商品_第71张图片

啥意思呢?目前还是找度娘线性布局Row和Column - 小图教父 - 博客园【关于这些涉及到木有接触过的知识点以后有时间整体单独成篇再细致的研究一下,目前先直接网上一大抄来解决疑惑】,如下:

运行:

最后运行看一下效果:

Flutter项目实战之女装商城------添加轮播图、分类导航、商品推荐、广告位处理、底部商品_第72张图片

那ios上运行效果是否也一样呢?这里开一个ios模拟器瞅一下:

嗯,还不错,只不过我电脑有点卡,录屏更卡,所以可以自行真机运行看一下性能怎么样~~

总结:

最后纵观一下对于这次首页功能的编写,发现整体的编写思路都是从局部的widget,再到全局的widget,一点点进行构建,比如拿商品推荐组件来说,它由标题和列表组成,于是乎将整体拆成了2个widget来进行实现:

Flutter项目实战之女装商城------添加轮播图、分类导航、商品推荐、广告位处理、底部商品_第73张图片

这样实现起来代码可读性也比较强,而不要将所有的实现全塞到这块:

Flutter项目实战之女装商城------添加轮播图、分类导航、商品推荐、广告位处理、底部商品_第74张图片

先学到这,下次继续~~

关注个人公众号,获得实时推送

你可能感兴趣的:(flutter项目实战,flutter)