Flutter有状态组件是UI中可根据时间变化而更新的部分,与无状态组件不同,拥有State对象,用于存储和管理组件的状态。
1 StatefulWidget
- 在Flutter中自定义组件其实就是一个类,这个类需要继承StatelessWidget或StatefulWidget。
- StatelessWidget,无状态组件,状态不可变的Widget,前期接触比较多。
- StatefulWidget,有状态组件,持有的状态可能在Widget生命周期中改变。
1-1 无状态计数器
- Android Studio使用快捷键
stless + Tab
,生成StatelessWidget代码块。
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(primarySwatch: Colors.blue),
home: HomePage(),
);
}
}
class HomePage extends StatelessWidget {
int countNum = 0;
HomePage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('无状态计数器')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('$countNum', style: Theme.of(context).textTheme.headline1),
const SizedBox(height: 100),
ElevatedButton(
onPressed: () {
// 增加时值会改变,页面不会变
countNum++;
// 在StatelessWidget中无法调用
// setState(() {countNum++;});
print(countNum);
},
child: const Text('增加'),
),
],
),
),
);
}
}
1-2 有状态计数器
- Android Studio使用快捷键
stful + Tab
,生成StatefulWidget代码块。
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(primarySwatch: Colors.blue),
home: const HomePage(),
);
}
}
// 有状态组件
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
int _numCount = 0;
@override
Widget build(BuildContext context) {
// 打印出来
print(_numCount);
return Scaffold(
appBar: AppBar(title: const Text('有状态计数器')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('$_numCount', style: Theme.of(context).textTheme.headline2),
const SizedBox(height: 60),
ElevatedButton(
onPressed: () {
setState(() {
_numCount++;
});
},
child: const Text('增加'),
),
],
),
),
);
}
}
1-3 实现浮动按钮
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(primarySwatch: Colors.blue),
home: const HomePage(),
);
}
}
// 有状态组件
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
int _numCount = 0;
@override
Widget build(BuildContext context) {
// 打印出来
print(_numCount);
return Scaffold(
appBar: AppBar(title: const Text('实现浮动按钮')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('$_numCount', style: Theme.of(context).textTheme.headline2),
const SizedBox(height: 60),
ElevatedButton(
onPressed: () {
setState(() {
_numCount++;
});
},
child: const Text('增加'),
),
],
),
),
// 浮动按钮
floatingActionButton: FloatingActionButton(
onPressed: () {
setState(() {
_numCount++;
});
},
// 按钮样式
child: const Icon(Icons.add),
),
);
}
}
1-4 实现动态列表
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(primarySwatch: Colors.blue),
home: const HomePage(),
);
}
}
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
// _list是私有属性,必须加final
final List<String> _list = [];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('实现动态列表')),
floatingActionButton: FloatingActionButton(
child: const Icon(Icons.add),
onPressed: () {
// 改变数据必须加上setState
setState(() {
_list.add('新增列表');
});
},
),
body: ListView(
// children: const [
// ListTile(title: Text('列表')),
// ListTile(title: Text('列表')),
// ListTile(title: Text('列表')),
// ],
children: _list.map((v) {
return ListTile(title: Text(v));
}).toList(),
),
);
}
}
2 自定义底部导航
- BottomNavigationBar,底部导航条,是Scaffold组件的参数。
- 参数说明
- items(List底部导航条按钮的集合)、currentIndex(默认选中第几个)。
- iconSize(icon)、onTap(选中变化回调函数)、fixedColor(选中颜色)。
- type(BottomNavigationBarType.fixed、BottomNavigationBarType.shifting)。
2-1 实现底部导航
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(primarySwatch: Colors.blue),
home: const Tabs(),
);
}
}
class Tabs extends StatefulWidget {
const Tabs({Key? key}) : super(key: key);
@override
State<Tabs> createState() => _TabsState();
}
class _TabsState extends State<Tabs> {
int _currentIndex = 0;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('实现底部导航')),
bottomNavigationBar: BottomNavigationBar(
// 默认选中分类(第二个)
// currentIndex: 1,
currentIndex: _currentIndex,
onTap: (index) {
// 获取点击的索引值
// print(index);
// 注意调用setState
setState(() {
_currentIndex = index;
});
},
items: const [
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: '首页',
),
BottomNavigationBarItem(
icon: Icon(Icons.category),
label: '分类',
),
BottomNavigationBarItem(
icon: Icon(Icons.settings),
label: '设置',
),
],
),
);
}
}
2-2 实现页面切换
# myflutter(项目名)目录结构如下
- pages/
- tabs/
- user.dart
- home.dart
- setting.dart
- category.dart
- tabs.dart
- main.dart
(1) user.dart
import 'package:flutter/material.dart';
class UserPage extends StatefulWidget {
const UserPage({Key? key}) : super(key: key);
@override
State<UserPage> createState() => _UserPageState();
}
class _UserPageState extends State<UserPage> {
@override
Widget build(BuildContext context) {
return const Center(
child: Text('用户信息'),
);
}
}
(2) tabs.dart
import './tabs/user.dart';
import './tabs/home.dart';
import './tabs/setting.dart';
import './tabs/category.dart';
import 'package:flutter/material.dart';
class Tabs extends StatefulWidget {
const Tabs({Key? key}) : super(key: key);
@override
State<Tabs> createState() => _TabsState();
}
class _TabsState extends State<Tabs> {
int _currentIndex = 0;
final List<Widget> _pages = const [
HomePage(),
CategoryPage(),
SettingPage(),
UserPage(),
];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('实现页面切换')),
body: _pages[_currentIndex],
bottomNavigationBar: BottomNavigationBar(
// 选中的颜色
fixedColor: Colors.red,
// 配置底部菜单大小
iconSize: 40,
// 4个以上菜单需配置此项
type: BottomNavigationBarType.fixed,
// 默认选中分类(第二个)
// currentIndex: 1,
currentIndex: _currentIndex,
onTap: (index) {
// 获取点击的索引值
// print(index);
// 注意调用setState
setState(() {
_currentIndex = index;
});
},
items: const [
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: '首页',
),
BottomNavigationBarItem(
icon: Icon(Icons.category),
label: '分类',
),
BottomNavigationBarItem(
icon: Icon(Icons.settings),
label: '设置',
),
// 没有type菜单图标会被挤掉
BottomNavigationBarItem(
icon: Icon(Icons.people),
label: '用户',
),
],
),
);
}
}
(3) main.dart
import './pages/tabs.dart';
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(primarySwatch: Colors.blue),
home: const Tabs(),
);
}
}
(4) home.dart
import 'package:flutter/material.dart';
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
@override
Widget build(BuildContext context) {
return const Center(
child: Text('首页'),
);
}
}
(5) setting.dart
import 'package:flutter/material.dart';
class SettingPage extends StatefulWidget {
const SettingPage({Key? key}) : super(key: key);
@override
State<SettingPage> createState() => _SettingPageState();
}
class _SettingPageState extends State<SettingPage> {
@override
Widget build(BuildContext context) {
return const Center(
child: Text('系统设置'),
);
}
}
(6) category.dart
import 'package:flutter/material.dart';
class CategoryPage extends StatefulWidget {
const CategoryPage({Key? key}) : super(key: key);
@override
State<CategoryPage> createState() => _CategoryPageState();
}
class _CategoryPageState extends State<CategoryPage> {
@override
Widget build(BuildContext context) {
// return const Center(
// child: Text('分类'),
// );
return ListView(
children: const [
ListTile(title: Text('列表1')),
ListTile(title: Text('列表2')),
ListTile(title: Text('列表3')),
],
);
}
}
3 凸起按钮的导航
- FloatingActionButton简称FAB,可实现浮动按钮,也可实现底部凸起导航,是Scaffold组件的参数。
- 属性说明
- elevation(未点击时的阴影)、hignlightElevation(点击时的阴影值,默认为12.0)。
- onPressed(点击事件回调)、shape(可定义FAB的形状等)、mini(是否为mini类型,默认false)。
- child(子视图,一般为Icon,不推荐使用文字)、tooltip(FAB被长按时显示,也是无障碍功能)。
# myflutter(项目名)目录结构如下,新增message.dart,除tabs.dart,其他内容不变
- pages/
- tabs/
- user.dart
- home.dart
- setting.dart
- message.dart【+】
- category.dart
- tabs.dart【🖊】
- main.dart
3-1 tabs.dart
import './tabs/user.dart';
import './tabs/home.dart';
import './tabs/setting.dart';
import './tabs/message.dart';
import './tabs/category.dart';
import 'package:flutter/material.dart';
class Tabs extends StatefulWidget {
const Tabs({Key? key}) : super(key: key);
@override
State<Tabs> createState() => _TabsState();
}
class _TabsState extends State<Tabs> {
int _currentIndex = 0;
final List<Widget> _pages = const [
HomePage(),
CategoryPage(),
MessagePage(),
SettingPage(),
UserPage(),
];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('凸起按钮的导航')),
body: _pages[_currentIndex],
bottomNavigationBar: BottomNavigationBar(
// 选中的颜色
fixedColor: Colors.red,
// 配置底部菜单大小
iconSize: 25,
// 4个以上菜单需配置此项
type: BottomNavigationBarType.fixed,
// 默认选中分类(第二个)
// currentIndex: 1,
currentIndex: _currentIndex,
onTap: (index) {
// 获取点击的索引值
// print(index);
// 注意调用setState
setState(() {
_currentIndex = index;
});
},
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: '设置',
),
// 没有type菜单图标会被挤掉
BottomNavigationBarItem(
icon: Icon(Icons.people),
label: '用户',
),
],
),
floatingActionButton: Container(
// 调整FloatingActionButton的大小
height: 60,
width: 60,
padding: const EdgeInsets.all(5),
// 调整FloatingActionButton的位置
margin: const EdgeInsets.only(top: 5),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(30),
),
child: FloatingActionButton(
backgroundColor: _currentIndex == 2 ? Colors.red : Colors.blue,
child: const Icon(Icons.add),
onPressed: () {
setState(() {
// 点击凸起+按钮会显示消息页面
_currentIndex = 2;
});
},
),
),
// 配置浮动按钮的位置
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
);
}
}
3-2 message.dart
import 'package:flutter/material.dart';
class MessagePage extends StatefulWidget {
const MessagePage({Key? key}) : super(key: key);
@override
State<MessagePage> createState() => _MessagePageState();
}
class _MessagePageState extends State<MessagePage> {
@override
Widget build(BuildContext context) {
return const Center(
child: Text('消息'),
);
}
}
4 Drawer抽屉视图
- Scaffold组件中传入drawer参数可定义左侧边栏,传入endDrawer可定义右侧边栏。
- 侧边栏默认是隐藏的,可通过手指滑动显示侧边栏,也可通过点击按钮显示侧边栏。
4-1 创建抽屉头部
- DrawerHeader属性:decoration(顶部背景颜色)、child(子元素)、padding(内边距)、margin(外边距)。
// tabs.dart【🖊】,其余内容不变
import './tabs/user.dart';
import './tabs/home.dart';
import './tabs/setting.dart';
import './tabs/message.dart';
import './tabs/category.dart';
import 'package:flutter/material.dart';
class Tabs extends StatefulWidget {
const Tabs({Key? key}) : super(key: key);
@override
State<Tabs> createState() => _TabsState();
}
class _TabsState extends State<Tabs> {
int _currentIndex = 0;
final List<Widget> _pages = const [
HomePage(),
CategoryPage(),
MessagePage(),
SettingPage(),
UserPage(),
];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('创建抽屉头部')),
drawer: Drawer(
child: Column(
children: [
Row(
children: [
Expanded(
flex: 1,
child: DrawerHeader(
decoration: const BoxDecoration(
color: Colors.blue,
image: DecorationImage(
image: NetworkImage(
'https://img0.baidu.com/it/u=1884790922,'
'3388645850&fm=253&fmt=auto&app=138&f=JP'
'EG?w=336&h=252',
),
fit: BoxFit.cover,
),
),
child: Column(
children: const [
ListTile(
leading: CircleAvatar(
backgroundImage: NetworkImage(
'https://img0.baidu.com/it/u=343412189,'
'4190523006&fm=253&fmt=auto&app=138&f=J'
'PEG?w=300&h=300',
),
),
title: Text(
'钱三两',
style: TextStyle(color: Colors.blue),
),
),
ListTile(title: Text('邮箱:Siren1996@qq.com')),
],
),
),
),
],
),
const ListTile(
leading: CircleAvatar(child: Icon(Icons.people)),
title: Text('个人中心'),
),
const Divider(),
const ListTile(
leading: CircleAvatar(child: Icon(Icons.settings)),
title: Text('系统设置'),
),
const Divider(),
],
),
),
endDrawer: const Drawer(child: Text('右侧侧边栏')),
body: _pages[_currentIndex],
bottomNavigationBar: BottomNavigationBar(
// 选中的颜色
fixedColor: Colors.red,
// 配置底部菜单大小
iconSize: 25,
// 4个以上菜单需配置此项
type: BottomNavigationBarType.fixed,
currentIndex: _currentIndex,
onTap: (index) {
setState(() {
_currentIndex = index;
});
},
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的大小
floatingActionButton: Container(
height: 60, width: 60, padding: const EdgeInsets.all(5),
// 调整FloatingActionButton的位置
margin: const EdgeInsets.only(top: 5),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(30),
),
child: FloatingActionButton(
backgroundColor: _currentIndex == 2 ? Colors.red : Colors.blue,
child: const Icon(Icons.add),
onPressed: () {
setState(() {
_currentIndex = 2;
});
},
),
),
// 配置浮动按钮的位置
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
);
}
}
4-2 账户抽屉头部
- UserAccountsDrawerHeader常见属性
- currentAccountPicture(账户头像)、otherAccountsPictures(用来设置当前账户的其他账户头像)。
- decoration(顶部背景颜色)、accountName(账户名)、accountEmail(账户邮箱)、margin(外边距)。
// tabs.dart【🖊】,其余内容不变
import './tabs/user.dart';
import './tabs/home.dart';
import './tabs/setting.dart';
import './tabs/message.dart';
import './tabs/category.dart';
import 'package:flutter/material.dart';
class Tabs extends StatefulWidget {
const Tabs({Key? key}) : super(key: key);
@override
State<Tabs> createState() => _TabsState();
}
class _TabsState extends State<Tabs> {
int _currentIndex = 0;
final List<Widget> _pages = const [
HomePage(),
CategoryPage(),
MessagePage(),
SettingPage(),
UserPage(),
];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('账户抽屉头部')),
drawer: Drawer(
child: Column(
children: [
Row(
children: [
Expanded(
flex: 1,
child: UserAccountsDrawerHeader(
accountName: const Text('钱三两'),
accountEmail: const Text('Siren1996@qq.com'),
otherAccountsPictures: [
Image.network(
'https://wx2.sinaimg.cn/mw690/714'
'ded47ly1g1xq9wc5uuj21e01e0jur.jpg',
),
Image.network(
'https://img1.baidu.com/it/u=2293783843,'
'2984695986&fm=253&fmt=auto&app=120&f=JP'
'EG?w=500&h=500',
),
Image.network(
'https://img1.baidu.com/it/u=2293783843,'
'2984695986&fm=253&fmt=auto&app=120&f=JP'
'EG?w=500&h=500',
),
],
currentAccountPicture: const CircleAvatar(
backgroundImage: NetworkImage(
'https://img0.baidu.com/it/u=343412189,'
'4190523006&fm=253&fmt=auto&app=138&f=J'
'PEG?w=300&h=300',
),
),
decoration: const BoxDecoration(
image: DecorationImage(
fit: BoxFit.cover,
image: NetworkImage(
'https://img0.baidu.com/it/u=1884790922,'
'3388645850&fm=253&fmt=auto&app=138&f=JP'
'EG?w=336&h=252',
),
),
),
),
),
],
),
const ListTile(
leading: CircleAvatar(child: Icon(Icons.people)),
title: Text('个人中心'),
),
const Divider(),
const ListTile(
leading: CircleAvatar(child: Icon(Icons.settings)),
title: Text('系统设置'),
),
const Divider(),
],
),
),
endDrawer: const Drawer(child: Text('右侧侧边栏')),
body: _pages[_currentIndex],
bottomNavigationBar: BottomNavigationBar(
// 选中的颜色
fixedColor: Colors.red,
// 配置底部菜单大小
iconSize: 25,
// 4个以上菜单需配置此项
type: BottomNavigationBarType.fixed,
currentIndex: _currentIndex,
onTap: (index) {
setState(() {
_currentIndex = index;
});
},
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的大小
floatingActionButton: Container(
height: 60, width: 60, padding: const EdgeInsets.all(5),
// 调整FloatingActionButton的位置
margin: const EdgeInsets.only(top: 5),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(30),
),
child: FloatingActionButton(
backgroundColor: _currentIndex == 2 ? Colors.red : Colors.blue,
child: const Icon(Icons.add),
onPressed: () {
setState(() {
_currentIndex = 2;
});
},
),
),
// 配置浮动按钮的位置
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
);
}
}
5 自定义顶部按钮
- AppBar自定义顶部按钮图标、颜色,属性如下。
- actions(通常使用IconButton来表示,可以放按钮组件)、iconTheme(图标样式)。
- bottom(通常放tabBar,标题下面显示一个Tab导航栏)、backgroundColor(导航的背景颜色)。
- title(标题,通常显示为当前界面的标题文字,可放组件)、centerTitle(标题是否居中显示)。
- leading(在标题前面显示的一个控件,通常为首页显示应用的logo,或其他界面的返回按钮)。
5-1 实现顶部按钮
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
// 去掉右上角dubug图标
debugShowCheckedModeBanner: false,
theme: ThemeData(primarySwatch: Colors.blue),
home: const HomePage(),
);
}
}
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
leading: IconButton(
icon: const Icon(Icons.menu),
onPressed: () {
print('左侧按钮图标');
},
),
// 导航的背景颜色
backgroundColor: Colors.red,
title: const Text('实现顶部按钮'),
// 右侧的按钮图标
actions: [
IconButton(
icon: const Icon(Icons.search),
onPressed: () {
print('搜索');
},
),
IconButton(
icon: const Icon(Icons.more_horiz),
onPressed: () {
print('更多');
},
),
],
),
);
}
}
5-2 顶部滑动导航
- TabBar常见属性,indicatorSize:指示器大小计算方式如下(两个等宽)。
- TabBarIndicatorSize.label(跟文字等宽)、TabBarIndicatorSize.tab(跟每个tab等宽)。
- unselectedLabelColor(未选中label颜色)、unselectedLabelStyle(未选中label的Style)。
- indicatorPadding(底部指示器的Padding)、indicator(指示器decoration,例如边框等)。
- tabs(显示的标签内容,一般使用Tab对象,也可以是其他组件)、isScrollable(是否可以滚动)。
- controller(TabController对象)、indicatorColor(指示器颜色)、indicatorWeight(指示器高度)。
- labelColor(选中label颜色)、labelStyle(选中label样式)、labelPadding(每个label的padding值)。
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
// 去掉右上角dubug图标
debugShowCheckedModeBanner: false,
theme: ThemeData(primarySwatch: Colors.blue),
home: const HomePage(),
);
}
}
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage>
with SingleTickerProviderStateMixin {
late TabController _tabController;
// 生命周期函数(组件初始化时触发)
@override
void initState() {
super.initState();
// length(导航标题)够多可左右滑动
_tabController = TabController(length: 5, vsync: this);
}
List<String> list = [];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
// 左侧的按钮图标
leading: IconButton(
icon: const Icon(Icons.menu),
onPressed: () {
print('左侧按钮图标');
},
),
// 导航的背景颜色
backgroundColor: Colors.red,
title: const Text('顶部滑动导航'),
// 右侧的按钮图标
actions: [
IconButton(
icon: const Icon(Icons.search),
onPressed: () {
print('搜索');
},
),
IconButton(
icon: const Icon(Icons.more_horiz),
onPressed: () {
print('更多');
},
),
],
bottom: TabBar(
// 设置可以左右滚动
isScrollable: true,
// 指示器颜色
indicatorColor: Colors.white,
// 指示器高度
indicatorWeight: 2,
// 底部指示器的Padding
indicatorPadding: const EdgeInsets.all(5),
// 与文字等宽,默认tab
// indicatorSize: TabBarIndicatorSize.label,
// 选中的label颜色
labelColor: Colors.yellow,
// 未选中的label颜色
unselectedLabelColor: Colors.white,
// 选中的label样式
labelStyle: const TextStyle(fontSize: 15),
unselectedLabelStyle: const TextStyle(fontSize: 12),
// 指示器边框
indicator: BoxDecoration(
color: Colors.blue,
// 边框角幅度
borderRadius: BorderRadius.circular(10),
),
// 配置controller需要去掉const
controller: _tabController,
tabs: const [
Tab(child: Text('关注')),
Tab(child: Text('热门')),
Tab(child: Text('视频')),
Tab(child: Text('图片')),
Tab(child: Text('推荐')),
],
),
),
body: TabBarView(
controller: _tabController,
children: [
// const Text('我是关注'),
ListView(children: const [ListTile(title: Text('动态关注'))]),
ListView(children: const [ListTile(title: Text('热门信息'))]),
ListView(children: const [ListTile(title: Text('视频加载'))]),
ListView(children: const [ListTile(title: Text('图片赏析'))]),
ListView(children: const [ListTile(title: Text('推荐列表'))]),
],
),
);
}
}
6 底部与顶部结合
- PreferredSize可以改变AppBar高度,AutomaticKeepAliveClientMixin可以快速实现页面缓存功能。
- 但是通过混入的方式实现不是很优雅,所以有必要对AutomaticKeepAliveClientMixin混入进行封装。
6-1 底部页面使用导航
# myflutter(项目名)目录结构如下,更改main.dart、tabs.dart、home.dart内容,其他不变
- pages/
- tabs/
- user.dart
- home.dart【🖊】
- setting.dart
- message.dart
- category.dart
- tabs.dart【🖊】
- main.dart【🖊】
(1) tabs.dart
import './tabs/user.dart';
import './tabs/home.dart';
import './tabs/setting.dart';
import './tabs/message.dart';
import './tabs/category.dart';
import 'package:flutter/material.dart';
class Tabs extends StatefulWidget {
const Tabs({Key? key}) : super(key: key);
@override
State<Tabs> createState() => _TabsState();
}
class _TabsState extends State<Tabs> {
int _currentIndex = 0;
final List<Widget> _pages = const [
HomePage(),
CategoryPage(),
MessagePage(),
SettingPage(),
UserPage(),
];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
elevation: 1,
backgroundColor: Colors.red,
title: const Text('底部页面使用导航'),
),
drawer: Drawer(
child: Column(
children: [
Row(
children: [
Expanded(
flex: 1,
child: UserAccountsDrawerHeader(
accountName: const Text('钱三两'),
accountEmail: const Text('Siren1996@qq.com'),
otherAccountsPictures: [
Image.network(
'https://wx2.sinaimg.cn/mw690/714'
'ded47ly1g1xq9wc5uuj21e01e0jur.jpg',
),
Image.network(
'https://img1.baidu.com/it/u=2293783843,'
'2984695986&fm=253&fmt=auto&app=120&f=JP'
'EG?w=500&h=500',
),
Image.network(
'https://img1.baidu.com/it/u=2293783843,'
'2984695986&fm=253&fmt=auto&app=120&f=JP'
'EG?w=500&h=500',
),
],
currentAccountPicture: const CircleAvatar(
backgroundImage: NetworkImage(
'https://img0.baidu.com/it/u=343412189,'
'4190523006&fm=253&fmt=auto&app=138&f=J'
'PEG?w=300&h=300',
),
),
decoration: const BoxDecoration(
image: DecorationImage(
fit: BoxFit.cover,
image: NetworkImage(
'https://img0.baidu.com/it/u=1884790922,'
'3388645850&fm=253&fmt=auto&app=138&f=JP'
'EG?w=336&h=252',
),
),
),
),
),
],
),
const ListTile(
leading: CircleAvatar(child: Icon(Icons.people)),
title: Text('个人中心'),
),
const Divider(),
const ListTile(
leading: CircleAvatar(child: Icon(Icons.settings)),
title: Text('系统设置'),
),
const Divider(),
],
),
),
// endDrawer: const Drawer(child: Text('右侧侧边栏')),
body: _pages[_currentIndex],
bottomNavigationBar: BottomNavigationBar(
// 选中的颜色
fixedColor: Colors.red,
// 配置底部菜单大小
iconSize: 25,
// 4个以上菜单需配置此项
type: BottomNavigationBarType.fixed,
currentIndex: _currentIndex,
onTap: (index) {
setState(() {
_currentIndex = index;
});
},
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的大小
floatingActionButton: Container(
height: 60, width: 60, padding: const EdgeInsets.all(5),
// 调整FloatingActionButton的位置
margin: const EdgeInsets.only(top: 5),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(30),
),
child: FloatingActionButton(
backgroundColor: _currentIndex == 2 ? Colors.red : Colors.blue,
child: const Icon(Icons.add),
onPressed: () {
setState(() {
_currentIndex = 2;
});
},
),
),
// 配置浮动按钮的位置
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
);
}
}
(2) main.dart
import './pages/tabs.dart';
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
// 去掉右上角的debug标识
debugShowCheckedModeBanner: false,
title: 'Flutter Demo',
theme: ThemeData(primarySwatch: Colors.blue),
home: const Tabs(),
);
}
}
(3) home.dart
import 'package:flutter/material.dart';
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage>
with SingleTickerProviderStateMixin {
late TabController _tabController;
@override
void initState() {
super.initState();
_tabController = TabController(length: 8, vsync: this);
}
@override
Widget build(BuildContext context) {
return Scaffold(
// 可以配置appBar的高度
appBar: PreferredSize(
preferredSize: const Size.fromHeight(40),
child: AppBar(
elevation: 0.5,
backgroundColor: Colors.white,
title: SizedBox(
// 修改TabBar的高度
height: 30,
child: TabBar(
labelStyle: const TextStyle(fontSize: 15),
isScrollable: true,
// 底部指示器的颜色
indicatorColor: Colors.red,
labelColor: Colors.red,
// label未选中的颜色
unselectedLabelColor: Colors.black,
indicatorSize: TabBarIndicatorSize.label,
controller: _tabController,
tabs: const [
Tab(child: Text('关注')),
Tab(child: Text('热门')),
Tab(child: Text('生活')),
Tab(child: Text('娱乐')),
Tab(child: Text('绘画')),
Tab(child: Text('设计')),
Tab(child: Text('编程')),
Tab(child: Text('职场')),
],
),
),
),
),
body: TabBarView(
controller: _tabController,
children: [
ListView(children: const [
ListTile(title: Text('关注列表01')),
ListTile(title: Text('关注列表02')),
ListTile(title: Text('关注列表03')),
ListTile(title: Text('关注列表04')),
ListTile(title: Text('关注列表05')),
ListTile(title: Text('关注列表06')),
ListTile(title: Text('关注列表07')),
ListTile(title: Text('关注列表08')),
ListTile(title: Text('关注列表09')),
ListTile(title: Text('关注列表10')),
ListTile(title: Text('关注列表11')),
ListTile(title: Text('关注列表12')),
ListTile(title: Text('关注列表13')),
ListTile(title: Text('关注列表14')),
ListTile(title: Text('关注列表15')),
ListTile(title: Text('关注列表16')),
ListTile(title: Text('关注列表17')),
ListTile(title: Text('关注列表18')),
ListTile(title: Text('关注列表19')),
ListTile(title: Text('关注列表20')),
]),
const Text('热门'),
const Text('生活'),
const Text('娱乐'),
const Text('绘画'),
const Text('设计'),
const Text('编程'),
const Text('职场'),
],
),
);
}
}
6-2 自定义的缓存组件
# myflutter(项目名)目录结构如下,新增KeepAliveWrapper.dart
# 更改tabs.dart、home.dart内容,其他不变
- pages/
- tabs/
- user.dart
- home.dart【🖊】
- setting.dart
- message.dart
- category.dart
- tabs.dart【🖊】
- tools/
- KeepAliveWrapper.dart【+】
- main.dart
(1) tabs.dart
import './tabs/user.dart';
import './tabs/home.dart';
import './tabs/setting.dart';
import './tabs/message.dart';
import './tabs/category.dart';
import 'package:flutter/material.dart';
class Tabs extends StatefulWidget {
const Tabs({Key? key}) : super(key: key);
@override
State<Tabs> createState() => _TabsState();
}
class _TabsState extends State<Tabs> {
int _currentIndex = 0;
final List<Widget> _pages = const [
HomePage(),
CategoryPage(),
MessagePage(),
SettingPage(),
UserPage(),
];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
elevation: 1,
backgroundColor: Colors.red,
title: const Text('自定义的缓存组件'),
),
drawer: Drawer(
child: Column(
children: [
Row(
children: [
Expanded(
flex: 1,
child: UserAccountsDrawerHeader(
accountName: const Text('钱三两'),
accountEmail: const Text('Siren1996@qq.com'),
otherAccountsPictures: [
Image.network(
'https://wx2.sinaimg.cn/mw690/714'
'ded47ly1g1xq9wc5uuj21e01e0jur.jpg',
),
Image.network(
'https://img2.baidu.com/it/u=3251345887,'
'2581092454&fm=253&fmt=auto&app=120&f=JP'
'EG?w=500&h=500',
),
Image.network(
'https://img1.baidu.com/it/u=2293783843,'
'2984695986&fm=253&fmt=auto&app=120&f=JP'
'EG?w=500&h=500',
),
],
currentAccountPicture: const CircleAvatar(
backgroundImage: NetworkImage(
'https://img0.baidu.com/it/u=343412189,'
'4190523006&fm=253&fmt=auto&app=138&f=J'
'PEG?w=300&h=300',
),
),
decoration: const BoxDecoration(
image: DecorationImage(
fit: BoxFit.cover,
image: NetworkImage(
'https://img0.baidu.com/it/u=1884790922,'
'3388645850&fm=253&fmt=auto&app=138&f=JP'
'EG?w=336&h=252',
),
),
),
),
),
],
),
const ListTile(
leading: CircleAvatar(child: Icon(Icons.people)),
title: Text('个人中心'),
),
const Divider(),
const ListTile(
leading: CircleAvatar(child: Icon(Icons.settings)),
title: Text('系统设置'),
),
const Divider(),
],
),
),
// endDrawer: const Drawer(child: Text('右侧侧边栏')),
body: _pages[_currentIndex],
bottomNavigationBar: BottomNavigationBar(
// 选中的颜色
fixedColor: Colors.red,
// 配置底部菜单大小
iconSize: 25,
// 4个以上菜单需配置此项
type: BottomNavigationBarType.fixed,
currentIndex: _currentIndex,
onTap: (index) {
setState(() {
_currentIndex = index;
});
},
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的大小
floatingActionButton: Container(
height: 60, width: 60, padding: const EdgeInsets.all(5),
// 调整FloatingActionButton的位置
margin: const EdgeInsets.only(top: 5),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(30),
),
child: FloatingActionButton(
backgroundColor: _currentIndex == 2 ? Colors.red : Colors.blue,
child: const Icon(Icons.add),
onPressed: () {
setState(() {
_currentIndex = 2;
});
},
),
),
// 配置浮动按钮的位置
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
);
}
}
(2) home.dart
import 'package:flutter/material.dart';
import '../../tools/KeepAliveWrapper.dart';
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage>
with SingleTickerProviderStateMixin {
late TabController _tabController;
@override
void initState() {
super.initState();
_tabController = TabController(length: 8, vsync: this);
}
@override
Widget build(BuildContext context) {
return Scaffold(
// 可以配置appBar的高度
appBar: PreferredSize(
preferredSize: const Size.fromHeight(40),
child: AppBar(
elevation: 0.5,
backgroundColor: Colors.white,
title: SizedBox(
// 修改TabBar的高度
height: 30,
child: TabBar(
labelStyle: const TextStyle(fontSize: 15),
isScrollable: true,
// 底部指示器的颜色
indicatorColor: Colors.red,
labelColor: Colors.red,
// label未选中的颜色
unselectedLabelColor: Colors.black,
indicatorSize: TabBarIndicatorSize.label,
controller: _tabController,
tabs: const [
Tab(child: Text('关注')),
Tab(child: Text('热门')),
Tab(child: Text('生活')),
Tab(child: Text('娱乐')),
Tab(child: Text('绘画')),
Tab(child: Text('设计')),
Tab(child: Text('编程')),
Tab(child: Text('职场')),
],
),
),
),
),
body: TabBarView(
controller: _tabController,
children: [
// KeepAliveWrapper自定义缓存组件
KeepAliveWrapper(
child: ListView(
children: const [
ListTile(title: Text('关注列表01')),
ListTile(title: Text('关注列表02')),
ListTile(title: Text('关注列表03')),
ListTile(title: Text('关注列表04')),
ListTile(title: Text('关注列表05')),
ListTile(title: Text('关注列表06')),
ListTile(title: Text('关注列表07')),
ListTile(title: Text('关注列表08')),
ListTile(title: Text('关注列表09')),
ListTile(title: Text('关注列表10')),
ListTile(title: Text('关注列表11')),
ListTile(title: Text('关注列表12')),
ListTile(title: Text('关注列表13')),
ListTile(title: Text('关注列表14')),
ListTile(title: Text('关注列表15')),
ListTile(title: Text('关注列表16')),
ListTile(title: Text('关注列表17')),
ListTile(title: Text('关注列表18')),
ListTile(title: Text('关注列表19')),
ListTile(title: Text('关注列表20')),
],
),
),
const Text('热门'), const Text('生活'), const Text('娱乐'),
const Text('绘画'),
const Text('设计'), const Text('编程'), const Text('职场'),
],
),
);
}
}
(3) KeepAliveWrapper.dart
import 'package:flutter/material.dart';
class KeepAliveWrapper extends StatefulWidget {
const KeepAliveWrapper(
{Key? key, @required this.child, this.keepAlive = true})
: super(key: key);
final Widget? child;
final bool keepAlive;
@override
State<KeepAliveWrapper> createState() => _KeepAliveWrapperState();
}
class _KeepAliveWrapperState extends State<KeepAliveWrapper>
with AutomaticKeepAliveClientMixin {
@override
Widget build(BuildContext context) {
return widget.child!;
}
@override
bool get wantKeepAlive => widget.keepAlive;
@override
void didUpdateWidget(covariant KeepAliveWrapper oldWidget) {
if (oldWidget.keepAlive != widget.keepAlive) {
// keepAlive状态需要更新,实现在AutomaticKeepAliveClientMixin中
updateKeepAlive();
}
super.didUpdateWidget(oldWidget);
}
}
6-3 获取选项卡索引值
# myflutter(项目名)目录结构如下,更改tabs.dart、home.dart内容,其他不变
- pages/
- tabs/
- user.dart
- home.dart【🖊】
- setting.dart
- message.dart
- category.dart
- tabs.dart【🖊】
- tools/
- KeepAliveWrapper.dart
- main.dart
(1) tabs.dart
import './tabs/user.dart';
import './tabs/home.dart';
import './tabs/setting.dart';
import './tabs/message.dart';
import './tabs/category.dart';
import 'package:flutter/material.dart';
class Tabs extends StatefulWidget {
const Tabs({Key? key}) : super(key: key);
@override
State<Tabs> createState() => _TabsState();
}
class _TabsState extends State<Tabs> {
int _currentIndex = 0;
final List<Widget> _pages = const [
HomePage(),
CategoryPage(),
MessagePage(),
SettingPage(),
UserPage(),
];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
elevation: 1,
backgroundColor: Colors.red,
title: const Text('获取选项卡索引值'),
),
drawer: Drawer(
child: Column(
children: [
Row(
children: [
Expanded(
flex: 1,
child: UserAccountsDrawerHeader(
accountName: const Text('钱三两'),
accountEmail: const Text('Siren1996@qq.com'),
otherAccountsPictures: [
Image.network(
'https://wx2.sinaimg.cn/mw690/714'
'ded47ly1g1xq9wc5uuj21e01e0jur.jpg',
),
Image.network(
'https://img2.baidu.com/it/u=3251345887,'
'2581092454&fm=253&fmt=auto&app=120&f=JP'
'EG?w=500&h=500',
),
Image.network(
'https://img1.baidu.com/it/u=2293783843,'
'2984695986&fm=253&fmt=auto&app=120&f=JP'
'EG?w=500&h=500',
),
],
currentAccountPicture: const CircleAvatar(
backgroundImage: NetworkImage(
'https://img0.baidu.com/it/u=343412189,'
'4190523006&fm=253&fmt=auto&app=138&f=J'
'PEG?w=300&h=300',
),
),
decoration: const BoxDecoration(
image: DecorationImage(
fit: BoxFit.cover,
image: NetworkImage(
'https://img0.baidu.com/it/u=1884790922,'
'3388645850&fm=253&fmt=auto&app=138&f=JP'
'EG?w=336&h=252',
),
),
),
),
),
],
),
const ListTile(
leading: CircleAvatar(child: Icon(Icons.people)),
title: Text('个人中心'),
),
const Divider(),
const ListTile(
leading: CircleAvatar(child: Icon(Icons.settings)),
title: Text('系统设置'),
),
const Divider(),
],
),
),
// endDrawer: const Drawer(child: Text('右侧侧边栏')),
body: _pages[_currentIndex],
bottomNavigationBar: BottomNavigationBar(
// 选中的颜色
fixedColor: Colors.red,
// 配置底部菜单大小
iconSize: 25,
// 4个以上菜单需配置此项
type: BottomNavigationBarType.fixed,
currentIndex: _currentIndex,
onTap: (index) {
setState(() {
_currentIndex = index;
});
},
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的大小
floatingActionButton: Container(
height: 60, width: 60, padding: const EdgeInsets.all(5),
// 调整FloatingActionButton的位置
margin: const EdgeInsets.only(top: 5),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(30),
),
child: FloatingActionButton(
backgroundColor: _currentIndex == 2 ? Colors.red : Colors.blue,
child: const Icon(Icons.add),
onPressed: () {
setState(() {
_currentIndex = 2;
});
},
),
),
// 配置浮动按钮的位置
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
);
}
}
(2) home.dart
import 'package:flutter/material.dart';
import '../../tools/KeepAliveWrapper.dart';
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage>
with SingleTickerProviderStateMixin {
late TabController _tabController;
@override
void initState() {
super.initState();
_tabController = TabController(length: 8, vsync: this);
// 监听_tabController的改变事件
_tabController.addListener(() {
// 获取两次
// print(_tabController.index);
if (_tabController.animation!.value == _tabController.index) {
// 获取索引值方法①:获取点击或滑动页面的索引值
print(_tabController.index);
}
});
}
// 组件销毁时触发
@override
// 例如点击底部导航的分类
void dispose() {
super.dispose();
// 销毁_tabController
_tabController.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
// 可以配置appBar的高度
appBar: PreferredSize(
preferredSize: const Size.fromHeight(40),
child: AppBar(
elevation: 0.5,
backgroundColor: Colors.white,
title: SizedBox(
// 修改TabBar的高度
height: 30,
child: TabBar(
labelStyle: const TextStyle(fontSize: 15),
isScrollable: true,
// 底部指示器的颜色
indicatorColor: Colors.red,
labelColor: Colors.red,
// label未选中的颜色
unselectedLabelColor: Colors.black,
indicatorSize: TabBarIndicatorSize.label,
controller: _tabController,
// 获取索引值方法②:只能监听点击事件,没法监听滑动
// onTap: (index) {
// print(index);
// },
tabs: const [
Tab(child: Text('关注')),
Tab(child: Text('热门')),
Tab(child: Text('生活')),
Tab(child: Text('娱乐')),
Tab(child: Text('绘画')),
Tab(child: Text('设计')),
Tab(child: Text('编程')),
Tab(child: Text('职场')),
],
),
),
),
),
body: TabBarView(
controller: _tabController,
children: [
// KeepAliveWrapper自定义缓存组件
KeepAliveWrapper(
child: ListView(
children: const [
ListTile(title: Text('关注列表01')),
ListTile(title: Text('关注列表02')),
ListTile(title: Text('关注列表03')),
ListTile(title: Text('关注列表04')),
ListTile(title: Text('关注列表05')),
ListTile(title: Text('关注列表06')),
ListTile(title: Text('关注列表07')),
ListTile(title: Text('关注列表08')),
ListTile(title: Text('关注列表09')),
ListTile(title: Text('关注列表10')),
ListTile(title: Text('关注列表11')),
ListTile(title: Text('关注列表12')),
ListTile(title: Text('关注列表13')),
ListTile(title: Text('关注列表14')),
ListTile(title: Text('关注列表15')),
ListTile(title: Text('关注列表16')),
ListTile(title: Text('关注列表17')),
ListTile(title: Text('关注列表18')),
ListTile(title: Text('关注列表19')),
ListTile(title: Text('关注列表20')),
],
),
),
const Text('热门'), const Text('生活'), const Text('娱乐'),
const Text('绘画'),
const Text('设计'), const Text('编程'), const Text('职场'),
],
),
);
}
}