🔥 本文由 程序喵正在路上 原创,CSDN首发!
💖 系列专栏:Flutter学习
🌠 首发时间:2024年5月26日
🦋 欢迎关注🖱点赞👍收藏🌟留言🐾
目录
- 有状态组件StatefulWidget
- 实现计数器功能
- 实现动态列表
- Scaffold属性:BottomNavigationBar
- 组件介绍
- 自定义底部导航
- 底部菜单选中
- 自定义底部导航实现页面切换
- Scaffold属性:FloatingActionButton
- FloatingActionButton详解
- 实现类似闲鱼App底部导航凸起按钮
- Scaffold属性:抽屉菜单Drawer
- DrawerHeader
- UserAccountsDrawerHeader
有状态组件StatefulWidget
在 Flutter 中自定义组件其实就是一个类,这个类需要继承 StatelessWidget
/ StatefulWidget
。
StatelessWidget
是无状态组件,状态不可变的 widgetStatefulWidget
是有状态组件,持有的状态可能在 widget 生命周期改变- 通俗的讲:如果我们想改变页面中的数据的话,这个时候就需要用到
StatefulWidget
实现计数器功能
import 'package:flutter/material.dart';void main() {runApp(const MyApp());
}class MyApp extends StatelessWidget {const MyApp({super.key});Widget build(BuildContext context) {return MaterialApp(title: 'Flutter Demo',theme: ThemeData(primarySwatch: Colors.blue,),home: const HomePage(),);}
}class HomePage extends StatefulWidget {const HomePage({super.key});State<HomePage> createState() => _HomePageState();
}class _HomePageState extends State<HomePage> {int _numCount = 0; //计数Widget build(BuildContext context) {return Scaffold(appBar: AppBar(title: const Text("flutter App"),),body: Center(child: Column(mainAxisAlignment: MainAxisAlignment.center,children: [Text("$_numCount",style: Theme.of(context).textTheme.headlineMedium,),const SizedBox(height: 60),ElevatedButton(onPressed: () {setState(() {_numCount++;});},child: const Text("增加"))],),),//Scaffold组件中自带的浮动按钮floatingActionButton: FloatingActionButton(onPressed: () {setState(() {_numCount++;});},child: const Icon(Icons.add),),);}
}
在使用 StatefulWidget
后,建议我们代码的框架写成上面这样,将 Scaffold
组件写到自定义组件中,这样我们可以使用其在有状态组件中的一些属性
实现动态列表
需求:实现通过点击按钮就能添加列表项的效果
class _HomePageState extends State<HomePage> {final List<String> _list = []; //final修饰的列表可以增加元素Widget build(BuildContext context) {return Scaffold(appBar: AppBar(title: const Text("动态列表"),),floatingActionButton: FloatingActionButton(onPressed: () {setState(() {_list.add("我是一个新增的列表");});},child: const Icon(Icons.add),),body: ListView(children: _list.map((e) {return ListTile(title: Text(e),);}).toList(),),);}
}
Scaffold属性:BottomNavigationBar
组件介绍
BottomNavigationBar
是底部导航条,可以让我们定义底部 Tab 切换,bottomNavigationBar
是 Scaffold
组件的参数。
BottomNavigationBar 常见的属性
属性名 | 说明 |
---|---|
items | List 底部导航条按钮集合 |
iconSize | icon |
currentIndex | 默认选中第几个 |
onTap | 选中变化回调函数 |
fixedColor | 选中的颜色 |
type | 底部超过4个菜单必须配置这个属性:ButtomNavigationBarType.fixed 、ButtomNavigationBarType.shifting |
自定义底部导航
实现如下效果:
class _HomePageState extends State<HomePage> {Widget build(BuildContext context) {return Scaffold(appBar: AppBar(title: const Text("自定义底部导航"),),body: const Center(child: Text("我是一个文本")),bottomNavigationBar: BottomNavigationBar(items: const [BottomNavigationBarItem(icon: Icon(Icons.home), label: "首页"),BottomNavigationBarItem(icon: Icon(Icons.category), label: "分类"),BottomNavigationBarItem(icon: Icon(Icons.settings), label: "设置"),],),);}
}
底部菜单选中
前面自定义的底部导航还没有完整实现,一般要求我们点击哪个菜单,哪个菜单就会显示选中的效果,下面我们来实现这个效果
class _HomePageState extends State<HomePage> {int _currentIndex = 0; //当前选中索引Widget build(BuildContext context) {return Scaffold(appBar: AppBar(title: const Text("自定义底部导航"),),body: const Center(child: Text("我是一个文本")),bottomNavigationBar: BottomNavigationBar(currentIndex: _currentIndex,fixedColor: Colors.blue,onTap: (value) {setState(() {_currentIndex = value;});},items: const [BottomNavigationBarItem(icon: Icon(Icons.home), label: "首页"),BottomNavigationBarItem(icon: Icon(Icons.category), label: "分类"),BottomNavigationBarItem(icon: Icon(Icons.settings), label: "设置"),],),);}
}
自定义底部导航实现页面切换
为了让代码条理更加清晰,我们将每个页面分文件写。
在 lib 目录下新建目录 pages,在 pages 下新建文件 tabs.dart 和文件夹 tabs,在 tabs 下新建 3 个页面对应的文件:
home.dart
import 'package:flutter/material.dart';class HomePage extends StatefulWidget {const HomePage({super.key});State<HomePage> createState() => _HomePageState();
}class _HomePageState extends State<HomePage> {Widget build(BuildContext context) {return const Center(child: Text("首页"),);}
}
category.dart
import 'package:flutter/material.dart';class CategoryPage extends StatefulWidget {const CategoryPage({super.key});State<CategoryPage> createState() => _CategoryPageState();
}class _CategoryPageState extends State<CategoryPage> {Widget build(BuildContext context) {return const Center(child: Text("分类"),);}
}
setting.dart
import 'package:flutter/material.dart';class SettingPage extends StatefulWidget {const SettingPage({super.key});State<SettingPage> createState() => _SettingPageState();
}class _SettingPageState extends State<SettingPage> {Widget build(BuildContext context) {return const Center(child: Text("系统设置"),);}
}
tabs.dart:前面页面切换的代码
import 'package:flutter/material.dart';
import './tabs/home.dart';
import './tabs/category.dart';
import './tabs/setting.dart';class Tabs extends StatefulWidget {const Tabs({super.key});State<Tabs> createState() => _TabsState();
}class _TabsState extends State<Tabs> {int _currentIndex = 0; //当前选中索引final List<Widget> _pages = const [HomePage(),CategoryPage(),SettingPage(),];Widget build(BuildContext context) {return Scaffold(appBar: AppBar(title: const Text("自定义底部导航"),),body: _pages[_currentIndex], //自动切换页面bottomNavigationBar: BottomNavigationBar(currentIndex: _currentIndex,fixedColor: Colors.blue,onTap: (value) {setState(() {_currentIndex = value;});},items: const [BottomNavigationBarItem(icon: Icon(Icons.home), label: "首页"),BottomNavigationBarItem(icon: Icon(Icons.category), label: "分类"),BottomNavigationBarItem(icon: Icon(Icons.settings), label: "设置"),],),);}
}
main.dart
import 'package:flutter/material.dart';
import 'pages/tabs.dart';void main() {runApp(const MyApp());
}class MyApp extends StatelessWidget {const MyApp({super.key});Widget build(BuildContext context) {return MaterialApp(title: 'Flutter Demo',theme: ThemeData(primarySwatch: Colors.blue,),debugShowCheckedModeBanner: false,home: const Tabs(),);}
}
Scaffold属性:FloatingActionButton
FloatingActionButton详解
FloatingActionButton
简称 FAB ,可以实现浮动按钮,也可以实现类似闲鱼app的底部凸起导航
属性名称 | 属性值 |
---|---|
child | 子视图,一般为 Icon,不推荐使用文字 |
tooltip | FAB被长按时显示,也是无障碍功能 |
backgroundColor | 背景颜色 |
elevation | 未点击时的阴影 |
highlightElevation | 点击时阴影值,默认12 |
onPressed | 点击事件回调 |
shape | 可以定义FAB的形状等 |
mini | 是否为 mini 类型,默认为 false |
实现类似闲鱼App底部导航凸起按钮
我们在前面自定义底部导航的代码稍加修改,再添加一个浮动按钮即可实现类似闲鱼底部导航效果:
这里给出 tabs.dart 的代码,其中 message.dart 和 user.dart 两个页面内容和其他页面类似
import 'package:flutter/material.dart';
import './tabs/home.dart';
import './tabs/category.dart';
import './tabs/setting.dart';
import './tabs/message.dart';
import './tabs/user.dart';class Tabs extends StatefulWidget {const Tabs({super.key});State<Tabs> createState() => _TabsState();
}class _TabsState extends State<Tabs> {int _currentIndex = 0; //当前选中索引final List<Widget> _pages = const [HomePage(),CategoryPage(),MessagePage(),SettingPage(),UserPage(),];Widget build(BuildContext context) {return Scaffold(appBar: AppBar(title: const Text("浮动按钮"),),body: _pages[_currentIndex], //自动切换页面bottomNavigationBar: BottomNavigationBar(type: BottomNavigationBarType.fixed,currentIndex: _currentIndex,fixedColor: Colors.blue,onTap: (value) {setState(() {_currentIndex = value;});},items: const [BottomNavigationBarItem(icon: Icon(Icons.home), label: "首页"),BottomNavigationBarItem(icon: Icon(Icons.category), label: "分类"),BottomNavigationBarItem(icon: Icon(Icons.message), label: "消息"),BottomNavigationBarItem(icon: Icon(Icons.settings), label: "设置"),BottomNavigationBarItem(icon: Icon(Icons.people), label: "用户")],),floatingActionButton: Container(height: 60,width: 60,padding: const EdgeInsets.all(4),margin: const EdgeInsets.only(top: 4),decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(30)),child: FloatingActionButton(shape: const CircleBorder(),backgroundColor:_currentIndex == 2 ? Colors.yellowAccent : Colors.blueAccent,onPressed: () {setState(() {_currentIndex = 2;});},child: const Icon(Icons.add),),),floatingActionButtonLocation:FloatingActionButtonLocation.centerDocked, //浮动按钮位置);}
}
Scaffold属性:抽屉菜单Drawer
在 Scaffold
组件里面传入 drawer
参数可以定义左侧边栏,传入 endDrawer
可以定义右侧边栏。侧边栏默认是隐藏的,我们可以通过手指滑动显示侧边栏,也可以通过点击按钮显示侧边栏。
DrawerHeader
DrawerHeader
是侧边栏的头部,常见属性如下:
属性 | 描述 |
---|---|
decoration | 设置顶部背景颜色 |
child | 配置子元素 |
padding | 内边距 |
margin | 外边距 |
在上一节代码 tabs.dart 的基础上,我们来给页面添加侧边栏
class _TabsState extends State<Tabs> {int _currentIndex = 0; //当前选中索引final List<Widget> _pages = const [HomePage(),CategoryPage(),MessagePage(),SettingPage(),UserPage(),];Widget build(BuildContext context) {return Scaffold(appBar: AppBar(title: const Text("浮动按钮"),),//左侧边栏drawer: Drawer(child: Column(children: [DrawerHeader(decoration: const BoxDecoration(color: Colors.blueAccent,image: DecorationImage(image: NetworkImage("https://xixi-web-tlias.oss-cn-guangzhou.aliyuncs.com/1.jpg"),fit: BoxFit.cover)),child: ListView(children: const [ListTile(leading: CircleAvatar(backgroundImage: NetworkImage("https://xixi-web-tlias.oss-cn-guangzhou.aliyuncs.com/2.jpg"),),title: Text("嘻嘻",style: TextStyle(color: Colors.white,),),),ListTile(title: Text("Email: 123456@qq.com",style: TextStyle(color: Colors.white,),),),],),),const ListTile(leading: CircleAvatar(child: Icon(Icons.people)),title: Text("个人中心"),),const Divider(),const ListTile(leading: CircleAvatar(child: Icon(Icons.settings)),title: Text("系统设置"),)],),),//右侧侧边栏endDrawer: Drawer(child: Column(children: [DrawerHeader(decoration: const BoxDecoration(color: Colors.blueAccent,image: DecorationImage(image: NetworkImage("https://xixi-web-tlias.oss-cn-guangzhou.aliyuncs.com/3.jpg"),fit: BoxFit.cover)),child: ListView(children: const [ListTile(leading: CircleAvatar(backgroundImage: NetworkImage("https://xixi-web-tlias.oss-cn-guangzhou.aliyuncs.com/4.jpg"),),title: Text("嘻嘻",style: TextStyle(color: Colors.white,),),),ListTile(title: Text("Email: 123456@qq.com",style: TextStyle(color: Colors.white,),),),],),),const ListTile(leading: CircleAvatar(child: Icon(Icons.people)),title: Text("个人中心"),),const Divider(),const ListTile(leading: CircleAvatar(child: Icon(Icons.settings)),title: Text("系统设置"),)],),),body: _pages[_currentIndex], //自动切换页面bottomNavigationBar: BottomNavigationBar(type: BottomNavigationBarType.fixed,currentIndex: _currentIndex,fixedColor: Colors.blue,onTap: (value) {setState(() {_currentIndex = value;});},items: const [BottomNavigationBarItem(icon: Icon(Icons.home), label: "首页"),BottomNavigationBarItem(icon: Icon(Icons.category), label: "分类"),BottomNavigationBarItem(icon: Icon(Icons.message), label: "消息"),BottomNavigationBarItem(icon: Icon(Icons.settings), label: "设置"),BottomNavigationBarItem(icon: Icon(Icons.people), label: "用户")],),floatingActionButton: Container(height: 60,width: 60,padding: const EdgeInsets.all(4),margin: const EdgeInsets.only(top: 4),decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(30)),child: FloatingActionButton(shape: const CircleBorder(),backgroundColor:_currentIndex == 2 ? Colors.yellowAccent : Colors.blueAccent,onPressed: () {setState(() {_currentIndex = 2;});},child: const Icon(Icons.add),),),floatingActionButtonLocation:FloatingActionButtonLocation.centerDocked, //浮动按钮位置);}
}
左右侧边栏只是名称不同,其他组件的使用都是一致的
UserAccountsDrawerHeader
像前面这样我们自己写侧边栏的头部内容,还是比较麻烦的,而且样式也不好调整,为此 flutter 给我们提供了 UserAccountsDrawerHeader
组件,其常用属性如下:
属性 | 描述 |
---|---|
decoration | 设置顶部背景颜色 |
accountName | 账户名称 |
accountEmail | 账户邮箱 |
currentAccountPicture | 用户头像 |
otherAccountsPictures | 用来设置当前账户其他账户头像 |
margin | 外边距 |
接下来,我们用 来写一个侧边栏
//左侧边栏
drawer: Drawer(child: Column(children: [UserAccountsDrawerHeader(accountName: const Text("嘻嘻"),accountEmail: const Text("123456@qq.com"),currentAccountPicture: const CircleAvatar(backgroundImage: NetworkImage("https://xixi-web-tlias.oss-cn-guangzhou.aliyuncs.com/1.jpg"),),decoration: const BoxDecoration(color: Colors.blue,image: DecorationImage(image: NetworkImage("https://xixi-web-tlias.oss-cn-guangzhou.aliyuncs.com/2.jpg"),fit: BoxFit.cover),),otherAccountsPictures: [Image.network("https://xixi-web-tlias.oss-cn-guangzhou.aliyuncs.com/3.jpg"),Image.network("https://xixi-web-tlias.oss-cn-guangzhou.aliyuncs.com/4.jpg"),Image.network("https://xixi-web-tlias.oss-cn-guangzhou.aliyuncs.com/5.jpg"),],),const ListTile(leading: CircleAvatar(child: Icon(Icons.people)),title: Text("个人中心"),),const Divider(),const ListTile(leading: CircleAvatar(child: Icon(Icons.settings)),title: Text("系统设置"),)],),
),