Flutter学习-项目实战
Posted GY-93
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Flutter学习-项目实战相关的知识,希望对你有一定的参考价值。
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端配置:
- 项目的目录结构划分
- 主题相关
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的外层添加一个SingleChildScrollView
widget即可,该Widget就可以使Colum进行滑动
2.6 flutter中实现抽屉效果
2.6.1 实现抽屉效果
在Flutter中如何实现抽屉效果, 其实很简单, flutter中给我们提供了一个属性drawer
来设置抽屉效果
2.6.2 如何控制抽屉效果的宽度
我们查看Drawer
widget的属性,并没有发现有属性直接设置宽度:
- 解决办法: 如何控制抽屉的宽度, 本身是没有一个属性来控制宽度, 我们可以在外面包裹一层Container,通过设置Conatiner的宽度来达到控制抽屉效果的看度
2.6.3 如何修改drawer的icon图标
我知道,在AppBar中有一个属性leading
,这个可以修改左侧导航栏的图标
但是当我们修改导航栏左侧icon
图标之后,我们发现点击按钮没有反应了,这个时候需要我们自己去打开 抽屉效果页面, 我们可以如下实现:
但是当我们点击按钮时,发现还是没有反应, 这是因为这句代码取的Scaffold
根本不是你自己创建的那个Scaffold
,因为你拿的是build
方法的context,会沿着该widget树往上寻找,但是你创建的Scaffold
是在这个widget的build
方法中, 所以是肯定找不到的
- 解决办法:
做如上修改之后, 抽屉效果页面就可以打开了
2.7 实现过滤功能
具体实现功能参考demo代码
以上是关于Flutter学习-项目实战的主要内容,如果未能解决你的问题,请参考以下文章