【Flutter从入门到实战】⑪、豆瓣案例-1、星星评分Widget、虚线Widget、TabbarWidget、BottomNavigationBarItem的封装、初始化配置抽取
Flutter从入门到实战
一共分为23个系列
①(Flutter、Dart环境搭建篇) 共3个内容 已更新
②(Dart语法1 篇) 共4个内容 已更新
③(Dart语法2 篇) 共2个内容 已更新
④(Flutter案例开发篇) 共4个内容 已更新
⑤(Flutter的StatelessWidget 共3个内容 已更新
⑥(Flutter的基础Widget篇) 共2个内容 已更新
⑦(布局Widget篇) 共1个内容 已更新
⑧(Flex、Row、Column以及Flexible、Stack篇) 共1个内容 已更新
⑨(滚动的Widget篇) 共4个内容 已更新
⑩(Dart的Future和网络篇) 共3个内容 已更新
⑪(豆瓣案例-1篇) 共3个内容 已更新
⑫(豆瓣案例-2篇) 共3个内容 已更新
官方文档说明
官方视频教程
Flutter的YouTube视频教程-小部件
⑪、豆瓣案例篇
- 项目搭建前配置
- 项目配置其他启动的main.dart
- ①、Flutter 星星评分Widget
- Flutter 星星评分Widget 带注释的代码
- Flutter 星星评分Widget 不带注释的代码
- Flutter 星星评分Widget使用方式
- 效果图 - Flutter 星星评分Widget 带注释的代码
- ②、Flutter 虚线Widget
- 效果图 Flutter 虚线Widget
- ③、Flutter TabbarWidget
- 1.main.dart
- 2. BottomNavigationBarItem 封装
- 3. 多Page页面封装
- 3.1 home.dart
- 3.2 home_content.dart
- 3.3 subject.dart
- 3.4 subject_content.dart
- 4.初始化配置
- 效果图 - Tabbar
项目搭建前配置
由于我的工程项目用于学习操作
为了方便管理 我直接在项目里面创建豆瓣项目的案例
但是不能同时启动两个main.dart
项目配置其他启动的main.dart
①、Flutter 星星评分Widget
封装成一个Widget 考虑用的是
StatelessWidget还是StateFulWidget
考虑点
- 这个Widget是否可以控制的。比如点击改变状态
- 如果是能点击、改变状态的话 就封装成FulWidget
- 如果只是单纯做展示的话 就封装成lessWidget
考虑点
需要传递什么
- 评分的图片
- 评分的评分数 当前分数、最大分数
- 评分的数量
- 评分的尺寸大小
- 评分之间的间距
- 评分的颜色 未选中、已选中
- 评分的点击事件
需要用到什么系统的Widget
Stack (全部星星的包裹)
Row (每个星星的整体)
Icon (星星的图标)
Text (星星的评分数)
CustomClipper
需要用到系统的裁剪功能比如 0.2星星的情况
Flutter 星星评分Widget 带注释的代码
// 1.星星评分Widget
class YHStarRating extends StatefulWidget {final double? rating; // 评分数final double maxRating; // 最大评分数final int count; // 评分总数量final double size; // 评分大小final Color unselectedColor; // 未选中颜色final Color selectedColor; // 选中的颜色final Widget unselectedImage; // 未选中图片final Widget selectedImage; // 选中图片YHStarRating({// 必传参数 this.rating 评分数@required this.rating,this.maxRating = 10,this.count = 5,this.size = 30,this.unselectedColor = const Color(0xffbbbbbb),this.selectedColor = const Color(0xffff0000),Widget? unselectedImage,Widget? selectedImage,}): this.unselectedImage = unselectedImage ?? Icon(Icons.star_border,color: unselectedColor,size: size),this.selectedImage = selectedImage ?? Icon(Icons.star, color: selectedColor, size: size);// 判断有没有图片传递进行。如果没有使用默认的Icon创建的星星// const YHStarRating({Key? key}) : super(key: key);@overrideState<YHStarRating> createState() => _YHStarRatingState();
}class _YHStarRatingState extends State<YHStarRating> {@overrideWidget build(BuildContext context) {return Stack(children: [builderStarRow(buildUnselectedStar()),builderStarRow(buildSelectedStar()),// Row(// // 由于内容是填充尽可能大 。所以需要设置内容为最小// mainAxisSize: MainAxisSize.min,// // 动态决定多少个星星 并且封装函数// children:buildUnselectedStar(),// ),// Row(// // 由于内容是填充尽可能大 。所以需要设置内容为最小// mainAxisSize: MainAxisSize.min,// children: buildSelectedStar(),// )],);}// 封装函数 提高阅读代码// 默认星星List<Widget> buildUnselectedStar(){return List.generate(widget.count, (index) {return widget.unselectedImage;// return Icon(Icons.star_border,color: widget.unselectedColor,size: widget.size);});}// 选中星星的函数List<Widget> buildSelectedStar() {// 案例// 比如一共是10分 那么 评分 8分 就是4个满星 1个空星// 比如一共是10分 那么 评分是 5 分 那么就是2个满星 一个半星// 先计算一个星是多少分// 总分数 / 总个数 = 每个星的分数// 10 / 5 = 每个星的分数// 1.创建starsList<Widget> stars = [];// final star = Icon(Icons.star, color: widget.selectedColor, size: widget.size);final star = widget.selectedImage;// 2. 构建满填充的 stardouble oneValue = widget.maxRating / widget.count;int entireCount = (widget.rating! / oneValue).floor(); // 使用向下取整 不管是4.9 都只能是4个for (var i = 0; i < entireCount; i++){stars.add(star);}// 3. 构建部分填充的 star 进行裁剪(ClipRect)// 计算部分填充的值// 总星星个数 - 总整个填充的// 比如总评分数是3.5 - 3 = 0.5// (widget.rating! / oneValue) - entireCount// 然后计算占据的比例// ((widget.rating! / oneValue) - entireCount) *widget.sizedouble leftWidth = ((widget.rating! / oneValue) - entireCount) * widget.size;final halfStar = ClipRect(clipper: YHStarClipper(leftWidth),child:star);stars.add(halfStar);// 满分超出范围的处理if(stars.length > widget.count){return stars.sublist(0,widget.count);// 裁剪}return stars;}// Row的函数抽取Row builderStarRow(List<Widget> children){return Row(mainAxisSize: MainAxisSize.min,children:children);}
}// 自定义裁剪
// 我们星星当做一个矩形来进行裁剪 所以需要在CustomClipper后面进行一个声明 Rect
class YHStarClipper extends CustomClipper<Rect>{double width;YHStarClipper(this.width);// 自定义裁剪 必须实现两个抽象方法@overrideRect getClip(Size size) {// TODO: implement getClip// 裁剪的位置return Rect.fromLTRB(0, 0, width, size.height); // size.height 是Widget的高度}// 重新裁剪@overridebool shouldReclip(covariant YHStarClipper oldClipper) {// TODO: implement shouldReclip// 当旧的宽度 和 当前的宽度不一致才需要重新裁剪return oldClipper.width != this.width;}
}
Flutter 星星评分Widget 不带注释的代码
// 1.星星评分Widget
class YHStarRating extends StatefulWidget {final double? rating; // 评分数final double maxRating; // 最大评分数final int count; // 评分总数量final double size; // 评分大小final Color unselectedColor; // 未选中颜色final Color selectedColor; // 选中的颜色final Widget unselectedImage; // 未选中图片final Widget selectedImage; // 选中图片YHStarRating({@required this.rating,this.maxRating = 10,this.count = 5,this.size = 30,this.unselectedColor = const Color(0xffbbbbbb),this.selectedColor = const Color(0xffff0000),Widget? unselectedImage,Widget? selectedImage,}): this.unselectedImage = unselectedImage ?? Icon(Icons.star_border,color: unselectedColor,size: size),this.selectedImage = selectedImage ?? Icon(Icons.star, color: selectedColor, size: size);@overrideState<YHStarRating> createState() => _YHStarRatingState();
}class _YHStarRatingState extends State<YHStarRating> {@overrideWidget build(BuildContext context) {return Stack(children: [builderStarRow(buildUnselectedStar()),builderStarRow(buildSelectedStar()),],);}// 封装函数 提高阅读代码// 默认星星List<Widget> buildUnselectedStar(){return List.generate(widget.count, (index) {return widget.unselectedImage;// return Icon(Icons.star_border,color: widget.unselectedColor,size: widget.size);});}// 选中星星的函数List<Widget> buildSelectedStar() {List<Widget> stars = [];final star = widget.selectedImage;double oneValue = widget.maxRating / widget.count;int entireCount = (widget.rating! / oneValue).floor(); for (var i = 0; i < entireCount; i++){stars.add(star);}double leftWidth = ((widget.rating! / oneValue) - entireCount) * widget.size;final halfStar = ClipRect(clipper: YHStarClipper(leftWidth),child:star);stars.add(halfStar);// 满分超出范围的处理if(stars.length > widget.count){return stars.sublist(0,widget.count);}return stars;}Row builderStarRow(List<Widget> children){return Row(mainAxisSize: MainAxisSize.min,children:children);}
}// 自定义裁剪
class YHStarClipper extends CustomClipper<Rect>{double width;YHStarClipper(this.width);@overrideRect getClip(Size size) {return Rect.fromLTRB(0, 0, width, size.height);}@overridebool shouldReclip(covariant YHStarClipper oldClipper) {return oldClipper.width != this.width;}
}
Flutter 星星评分Widget使用方式
class YHiOSHomePage extends StatelessWidget {@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text("豆瓣"),),body: Center(// 评分数、最大评分数、星星个数child: YHStarRating(rating: 7,maxRating: 5, count: 5),),);}
}
效果图 - Flutter 星星评分Widget 带注释的代码
②、Flutter 虚线Widget
考虑点
- 虚线的方向
- 虚线的方向的宽度
- 虚线的方向的高度
- 虚线的颜色
使用到系统的Widget
- 方向
- sabox 每一根虚线
class YHiOSHomePage extends StatelessWidget {@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text("豆瓣"),),body: Center(child:Column(mainAxisAlignment: MainAxisAlignment.center,children: [Container(width: 200,child: YHDasheLine(dashedWidth: 8,count: 10),),Container(height: 200,child: YHDasheLine(axis: Axis.vertical, dashedHeight: 5,count: 15),),],),),);}
}// 1.虚线
// 只负责展示 所以直接使用StatelessWidget 即可
class YHDasheLine extends StatelessWidget {// 1.提供的方向final Axis axis;final double dashedWidth; // 虚线的宽度final double dashedHeight; // 虚线的高度final int count; // 虚线的数量final Color color; // 虚线的颜色YHDasheLine({this.axis = Axis.horizontal,this.dashedWidth = 1,this.dashedHeight = 1,this.count = 10,this.color = Colors.red,});@overrideWidget build(BuildContext context) {return Flex(direction: axis,// 间距 使用 主轴对齐方式mainAxisAlignment: MainAxisAlignment.spaceBetween,// 用不上索引 使用 _ tichildren: List.generate(count, ( _ ) {return SizedBox(width: dashedWidth,height: dashedHeight,// SizedBox没有 颜色属性 使用 Child 给SizedBox进行一个装饰child: DecoratedBox(decoration: BoxDecoration(color: color),),);}),);}
}
效果图 Flutter 虚线Widget
③、Flutter TabbarWidget
考虑点
使用系统哪些Widget去实现
- Stack 能将内容进行包裹
- 使用 IndexedStack 进行包裹page页面
- 进行main、初始化配置、多个page页面进行抽取
总结
item的封装、页面和页面内容的封装、初始化列表的封装
如果有网络请求、UI 建议多个文件夹管理(core,UI)
1.main.dart
import 'package:flutter/material.dart';
import 'package:learn_flutter/douban/pages/main/bottom_bar_item.dart';
// import 'package:learn_flutter/douban/pages/main/initialize_item.dart';// 绝对引入
import 'initialize_item.dart';// 相对引入 相当于在main.dart这个文件夹 引入initialize_item.dart 说明 initialize_item.dart和main.dart在同一个文件夹class YHMainPage extends StatefulWidget {@overrideState<YHMainPage> createState() => _YHMainPageState();
}class _YHMainPageState extends State<YHMainPage> {int _currentIndex = 0; // 记录当前tabbar点击的索引@overrideWidget build(BuildContext context) {return Scaffold(body: IndexedStack(index: _currentIndex,children: config_pages),bottomNavigationBar: BottomNavigationBar(// 点击选择默认字体会有一个放大和缩小// 因为系统设置 选中为 14 默认为 12selectedFontSize: 14,unselectedFontSize: 14,// selectedItemColor: Colors.red,// unselectedItemColor: Colors.blue,// item点击 记录切换currentIndex: _currentIndex,// 当item 超过4个就需要设置typetype: BottomNavigationBarType.fixed,items: config_items,onTap: (index){setState(() {_currentIndex = index;});},),);}//1.BottomNavigationBarItem 封装函数BottomNavigationBarItem buildBottomItem(iconName,title){return BottomNavigationBarItem(label: title,icon: Image.asset("assets/images/tabbar/$iconName.png",width: 32),activeIcon: Image.asset("assets/images/tabbar/${iconName}_active.png",width: 32));}
}// 2.BottomNavigationBarItem 封装类
2. BottomNavigationBarItem 封装
import 'package:flutter/material.dart';class YHBottomBarItem extends BottomNavigationBarItem {YHBottomBarItem(String iconName,String title):super(label: title,icon: Image.asset("assets/images/tabbar/$iconName.png",width: 32),activeIcon: Image.asset("assets/images/tabbar/${iconName}_active.png",width: 32),);
}
3. 多Page页面封装
3.1 home.dart
import 'package:flutter/material.dart';
import 'home_content.dart';class YHHomePage extends StatelessWidget {@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text("首页"),),body: YHHomeContent(),);}
}
3.2 home_content.dart
import 'package:flutter/material.dart';class YHHomeContent extends StatefulWidget {@overrideState<YHHomeContent> createState() => _YHHomeContentState();
}class _YHHomeContentState extends State<YHHomeContent> {@overrideWidget build(BuildContext context) {return Center(child: Text("首页内容",style: TextStyle(fontSize: 30,color: Colors.red)),);}
}
3.3 subject.dart
import 'package:flutter/material.dart';
import 'subject_content.dart';class YHSubjectPage extends StatelessWidget {@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text("首页"),),body: YHSubjectContent(),);}
}
3.4 subject_content.dart
import 'package:flutter/material.dart';class YHSubjectContent extends StatefulWidget {@overrideState<YHSubjectContent> createState() => _YHHomeContentState();
}class _YHHomeContentState extends State<YHSubjectContent> {@overrideWidget build(BuildContext context) {return Center(child: Text("书影音内容",style: TextStyle(fontSize: 30,color: Colors.red)),);}
}
4.初始化配置
import 'package:flutter/cupertino.dart';
import 'package:learn_flutter/douban/pages/home/home.dart';
import '../subject/subject.dart';
import 'bottom_bar_item.dart';// 初始化抽取// 1. tabbar item
List<YHBottomBarItem> config_items = [YHBottomBarItem("home", "首页"),YHBottomBarItem("subject", "书影音"),YHBottomBarItem("group", "小组"),YHBottomBarItem("mall", "市集"),YHBottomBarItem("profile", "我的"),
];// 1. 每个tabbar的页面
List<Widget> config_pages = [YHHomePage(),YHSubjectPage(),YHHomePage(),YHSubjectPage(),YHHomePage(),];
效果图 - Tabbar
【Flutter从入门到实战】⑪、豆瓣案例-1、星星评分Widget、虚线Widget、TabbarWidget、BottomNavigationBarItem的封装、初始化配置抽取相关推荐
- 【Flutter从入门到实战】 ⑨、滚动的Widget-ListView、GridView、SliverWidget、滚动的Widget的滚动监听的方式
Flutter从入门到实战 一共分为23个系列 ①(Flutter.Dart环境搭建篇) 共3个内容 已更新 ②(Dart语法1 篇) 共4个内容 已更新 ③(Dart语法2 篇) 共2个内容 已更新 ...
- 【Flutter从入门到实战】 ⑧、FlexWidget、Flex的主轴和交叉轴、Row和Column的使用、Expanded的FlexFit和flex的使用、Stack、Positioned的使用
Flutter从入门到实战 一共分为23个系列 ①(Flutter.Dart环境搭建篇) 共3个内容 已更新 ②(Dart语法1 篇) 共4个内容 已更新 ③(Dart语法2 篇) 共2个内容 已更新 ...
- 新书推荐 | Flutter技术入门与实战(第2版)
新书推荐 <Flutter技术入门与实战(第2版)> 长按二维码 了解及购买 从实战角度出发,手把手教会Flutter,案例丰富,实操性强. 编辑推荐 本书在上一版的基础上,根据Flutt ...
- Flutter+Dart入门与实战套餐(含电子商城系统)
套餐(全): https://ke.qq.com/course/package/23970?tuin=2c77f7f0 课程目标 掌握Dart语言及Flutter跨平台开发技术 适用人群 零基础,小白 ...
- PyTorch深度学习入门与实战(案例视频精讲)
作者:孙玉林,余本国 著 出版社:中国水利水电出版社 品牌:智博尚书 出版时间:2020-07-01 PyTorch深度学习入门与实战(案例视频精讲)
- Flutter从入门到实战(一)之环境搭建(Mac版)
Flutter从入门到实战(一)之环境搭建(Mac版) Flutter官网镇楼 Flutter SDK 下载 镜像使用 运行 flutter doctor iOS环境配置 Android环境配置 下载 ...
- Jetson Nano 从入门到实战(案例:Opencv配置、人脸检测、二维码检测)
目录 1. Jetson Nano简介 2. Jetson Nano环境配置 2.1 开箱配件介绍 2.2 烧录系统 2.3 开机和基本设置 2.4 开发环境配置 2.4.1 更新源和软件 2.4.2 ...
- python编程入门与案例详解pdf-Flutter技术入门与实战 PDF 清晰版
给大家带来的一篇关于Flutter相关的电子书资源,介绍了关于Flutter入门.Flutter实战.Flutter技术方面的内容,本书是由机械工业出版社出版,格式为PDF,资源大小162.5 MB, ...
- 从入门到实战,Netty多线程篇案例集锦
从入门到实战,Netty多线程篇案例集锦 原创 2015-09-10 李林峰 InfoQ Netty案例集锦系列文章介绍 1|Netty的特点 Netty入门比较简单,主要原因有如下几点: Netty ...
最新文章
- EXTJS布局示例(panel,Viewport,TabPanel)
- .NET Core开发实战(第7课:用Autofac增强容器能力)--学习笔记(下)
- MATLAB图像中添加标记
- 《软件需求分析(第二版)》第 14 章——需求管理的原则和实践 重点部分总结
- 利用SSL协议配置web服务器,基于windows2008平台配置实现利用SSL协议的安全IISWeb服务器汇总.doc...
- informix sybase数据库下载地址
- 红巨星转场特效预设AE插件 Red Giant Universe 6.0.1 WIN
- 百度离线地图瓦片图制作
- 大学计算机音乐一起学,和学生一起学音乐
- 土巴兔CEO王国彬:这五点,决定了你事业的上限
- Educational Codeforces Round 123 (Rated for Div. 2)(ABCDE)
- 并行与分布式计算导论(七)MPI Collective Communication
- latex制作三线表
- 微信小程序云开发-内容管理系统(CMS)小案例
- Android APP开发框架选择——JSBridge既个人心得和技巧
- CAcls命令在提权中的使用
- win10linux远程命令,IT之家学院:在Win10下管理远程命令行
- java蓝桥杯——矩形面积交
- VCIP 2020:面向VVC的自适应分辨率改变
- office2003稿纸加载项的删除解决方法