Dialog弹窗,用于显示信息、提示用户或获取用户输入,PageView则是一个可滑动的页面视图,用于展示多页面切换效果。
1 Dialog弹出窗口
- AlertDialog(弹窗提示信息)、SimpleDialog(弹窗选择信息)、showModalBottomSheet(底部弹出窗口)。
- 下载包:到pubspec.yaml的
dependencies
中加入fluttertoast: ^8.2.4
,保存后点Pub get
下载。 - 如果报错,参看:uses-sdk:minSdkVersion 16 cannot be smaller than version 19 declared in library。
1-1 内置的Dialog
# myflutter(项目名)目录结构如下
- pages/
- tabs/
- user.dart
- home.dart
- setting.dart
- category.dart
- tabs.dart
- dialog.dart
- routes/
- routes.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 {
final int index;
const Tabs({Key? key, this.index = 0}) : super(key: key);
@override
State<Tabs> createState() => _TabsState();
}
class _TabsState extends State<Tabs> {
late int _currentIndex;
@override
void initState() {
super.initState();
_currentIndex = widget.index;
}
final List<Widget> _pages = const [
HomePage(),
CategoryPage(),
SettingPage(),
UserPage(),
];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('内置的Dialog')),
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/routes/routes.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,
// 全局配置主题
appBarTheme: const AppBarTheme(
// 主题标题全部居中显示
centerTitle: true,
),
),
// home: const Tabs(),
// 初始化路由
initialRoute: '/',
// 固定写法,配置onGenerateRoute
onGenerateRoute: onGenerateRoute,
);
}
}
(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 Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () {
Navigator.pushNamed(context, '/dialog');
},
child: const Text('Dialog演示'),
),
],
),
);
}
}
(5) dialog.dart
import 'package:flutter/material.dart';
import 'package:fluttertoast/fluttertoast.dart';
class DialogPage extends StatefulWidget {
const DialogPage({Key? key}) : super(key: key);
@override
State<DialogPage> createState() => _DialogPageState();
}
class _DialogPageState extends State<DialogPage> {
void _alertDialog() async {
// print('_alertDialog');
var result = await showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: const Text('提示信息!'),
content: const Text('您确定要删除吗?'),
actions: [
TextButton(
onPressed: () {
print('OK');
// 点击按钮关闭窗口
Navigator.of(context).pop('OK');
},
child: const Text('确定'),
),
TextButton(
onPressed: () {
print('Cancel');
// 外层result打印信息
Navigator.of(context).pop('Cancel');
},
child: const Text('取消'),
),
],
);
},
);
print(result);
}
void _simpleDialog() async {
// barrierDismissible
// print('_simpleDialog');
// 点灰色背景,弹窗不消失
var result = await showDialog(
barrierDismissible: false,
context: context,
builder: (context) {
return SimpleDialog(
title: const Text('请选择语言:'),
children: [
SimpleDialogOption(
// Navigator.of().pop()方法一
onPressed: () {
print('汉语');
Navigator.of(context).pop('汉语');
},
child: const Text('汉语'),
),
const Divider(),
SimpleDialogOption(
// Navigator.pop()方法二
onPressed: () {
print('英语');
Navigator.pop(context, '英语');
},
child: const Text('英语'),
),
const Divider(),
SimpleDialogOption(
// pop不传第二个参数值返回null
onPressed: () {
print('日语');
Navigator.pop(context, '日语');
},
child: const Text('日语'),
),
const Divider(),
],
);
},
);
print('----');
print(result);
}
void _modelBottomSheet() async {
// print('_modelBottomSheet');
var result = await showModalBottomSheet(
context: context,
builder: (context) {
return SizedBox(
height: 200,
child: Column(
children: [
ListTile(
title: const Text('分享'),
onTap: () {
print('分享');
Navigator.pop(context, '分享');
},
),
const Divider(),
ListTile(
title: const Text('收藏'),
onTap: () {
print('收藏');
Navigator.pop(context, '收藏');
},
),
const Divider(),
ListTile(
title: const Text('取消'),
onTap: () {
print('取消');
Navigator.pop(context, '取消');
},
),
],
),
);
},
);
print('****');
print(result);
}
void _toast() {
// print('_toast');
// 使用该功能,需要重新flutter run
Fluttertoast.showToast(
msg: '提示信息',
// 值针对Android平台,1秒消失
toastLength: Toast.LENGTH_SHORT,
// 5秒消失
// toastLength: Toast.LENGTH_LONG,
// 调整方位
gravity: ToastGravity.TOP,
// 提示时间,针对iOS和Web
timeInSecForIosWeb: 1,
// 背景颜色
backgroundColor: Colors.black,
// 文本颜色
textColor: Colors.white,
// 文本字体大小
fontSize: 16.0,
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Dialog')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
ElevatedButton(
onPressed: _alertDialog,
child: const Text('弹窗提示信息:AlertDialog'),
),
const SizedBox(height: 20),
ElevatedButton(
onPressed: _simpleDialog,
child: const Text('弹窗选择信息:SimpleDialog'),
),
const SizedBox(height: 20),
ElevatedButton(
onPressed: _modelBottomSheet,
child: const Text('底部弹窗:showModalBottomSheet'),
),
const SizedBox(height: 20),
ElevatedButton(onPressed: _toast, child: const Text('Toast')),
],
),
),
);
}
}
(6) routes.dart
import '../tabs.dart';
import '../dialog.dart';
// 配置iOS风格的路由,引入库
import 'package:flutter/cupertino.dart';
// import 'package:flutter/material.dart';
// 定义一个Map类型的路由
Map routes = {
'/': (context) => const Tabs(),
'/dialog': (context) => const DialogPage(),
};
// 配置onGenerateRoute,固定写法,该方法相当于一个中间件,可以做权限判断
// ignore: prefer_function_declarations_over_variables
var onGenerateRoute = (RouteSettings settings) {
// 统一处理
final String? name = settings.name;
final Function? pageContentBuilder = routes[name];
if (pageContentBuilder != null) {
if (settings.arguments != null) {
// final Route route = MaterialPageRoute(
// 替换MaterialPageRoute
final Route route = CupertinoPageRoute(
builder:
(context) =>
pageContentBuilder(context, arguments: settings.arguments),
);
return route;
} else {
// final Route route = MaterialPageRoute(
final Route route = CupertinoPageRoute(
builder: (context) => pageContentBuilder(context),
);
return route;
}
}
return null;
};
(7) 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('系统设置'));
}
}
(8) 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('分类页面'));
}
}
1-2 自定义Dialog
- 自定义Dialog需继承Dialog类,它所提供的child用于编写视图界面,可能达不到想要的效果。
- 因为默认的Dialog背景框是满屏展示的,如果想要完全自定义界面,就必须得重写build函数。
# myflutter(项目名)目录结构如下,修改“内置的Dialog”代码,新增myDialog.dart
- pages/
- tabs/
- user.dart
- home.dart
- setting.dart
- category.dart
- tabs.dart【🖊】
- dialog.dart【🖊】
- routes/
- routes.dart
- widget/
- myDialog.dart【+】
- main.dart
(1) 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 {
final int index;
const Tabs({Key? key, this.index = 0}) : super(key: key);
@override
State<Tabs> createState() => _TabsState();
}
class _TabsState extends State<Tabs> {
late int _currentIndex;
@override
void initState() {
super.initState();
_currentIndex = widget.index;
}
final List<Widget> _pages = const [
HomePage(),
CategoryPage(),
SettingPage(),
UserPage(),
];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('自定义Dialog')),
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: '用户'),
],
),
);
}
}
(2) dialog.dart
import './widget/myDialog.dart';
import 'package:flutter/material.dart';
class DialogPage extends StatefulWidget {
const DialogPage({Key? key}) : super(key: key);
@override
State<DialogPage> createState() => _DialogPageState();
}
class _DialogPageState extends State<DialogPage> {
void _myDialog() async {
// print('_myDialog');
var result = await showDialog(
// 点灰色背景,弹窗不消失
barrierDismissible: false,
context: context,
builder: (context) {
return MyDialog(
title: '提示!',
content:
'采采流水,蓬蓬远春。窈窕深谷,时见美人。\n'
'碧桃满树,风日水滨。柳阴路曲,流莺比邻。\n'
'乘之愈往,识之愈真。如将不尽,与古为新。',
opTap: () {
print('关闭');
Navigator.of(context).pop('关闭按钮事件');
},
);
},
);
print(result);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Dialog')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
ElevatedButton(
// 不加括号表示注册方法
onPressed: _myDialog,
// 加括号表示调用方法
// onPressed: _myDialog(),
child: const Text('自定义Dialog'),
),
],
),
),
);
}
}
(3) myDialog.dart
import 'package:flutter/material.dart';
// 继承Dialog类
class MyDialog extends Dialog {
// 外部传值设置
final String title;
final String content;
final Function()? opTap;
const MyDialog({
Key? key,
required this.title,
required this.content,
required this.opTap,
}) : super(key: key);
@override
// 实现build方法
Widget build(BuildContext context) {
return Material(
// 设置背景透明
type: MaterialType.transparency,
// 用于包裹组件,否则全屏展示
child: Center(
child: Container(
height: 200,
width: 320,
color: Colors.white,
child: Column(
children: [
Padding(
padding: const EdgeInsets.all(10),
child: Stack(
children: [
Align(
alignment: Alignment.centerLeft,
child: Text(title, style: const TextStyle(fontSize: 18)),
),
Align(
alignment: Alignment.centerRight,
// child: Icon(Icons.close),
child: InkWell(
child: const Icon(Icons.close),
// onTap: () {print('close'); Navigator.pop(context);},
onTap: opTap,
),
),
],
),
),
const Divider(),
Container(
padding: const EdgeInsets.all(20),
width: double.infinity,
child: Text(content, style: const TextStyle(fontSize: 13)),
),
],
),
),
),
);
}
}
2 PageView的使用
- Flutter中的轮动图以及上下滑页切换视频功能等,都可以通过PageView实现。
- 常见属性
- children(配置子元素)、scrollDirection(Axis.horizonta水平方向,Axis.vertical垂直方向)。
- onPageChanged(page改变的时候触发)、allowImpLicitScrolling(缓存当前页面的前后两页)。
# myflutter(项目名)目录结构如下,修改“内置的Dialog”代码
# 新增pageView.dart,去掉dialog.dart
- pages/
- tabs/
- user.dart
- home.dart【🖊】
- setting.dart
- category.dart
- tabs.dart【🖊】
- pageView.dart【+】
- routes/
- routes.dart【🖊】
- main.dart
2-1 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 {
final int index;
const Tabs({Key? key, this.index = 0}) : super(key: key);
@override
State<Tabs> createState() => _TabsState();
}
class _TabsState extends State<Tabs> {
late int _currentIndex;
@override
void initState() {
super.initState();
_currentIndex = widget.index;
}
final List<Widget> _pages = const [
HomePage(),
CategoryPage(),
SettingPage(),
UserPage(),
];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('PageView的使用')),
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: '用户'),
],
),
);
}
}
2-2 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 Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () {
Navigator.pushNamed(context, '/pageView');
},
child: const Text('PageView演示'),
),
],
),
);
}
}
2-3 routes.dart
import '../tabs.dart';
import '../pageView.dart';
// 配置iOS风格的路由,引入库
import 'package:flutter/cupertino.dart';
// import 'package:flutter/material.dart';
// 定义一个Map类型的路由
Map routes = {
'/': (context) => const Tabs(),
'/pageView': (context) => const PageViewPage(),
};
// 配置onGenerateRoute,固定写法,该方法相当于一个中间件,可以做权限判断
// ignore: prefer_function_declarations_over_variables
var onGenerateRoute = (RouteSettings settings) {
// 统一处理
final String? name = settings.name;
final Function? pageContentBuilder = routes[name];
if (pageContentBuilder != null) {
if (settings.arguments != null) {
// final Route route = MaterialPageRoute(
// 替换MaterialPageRoute
final Route route = CupertinoPageRoute(
builder:
(context) =>
pageContentBuilder(context, arguments: settings.arguments),
);
return route;
} else {
// final Route route = MaterialPageRoute(
final Route route = CupertinoPageRoute(
builder: (context) => pageContentBuilder(context),
);
return route;
}
}
return null;
};
2-4 pageView.dart
import 'package:flutter/material.dart';
class PageViewPage extends StatefulWidget {
const PageViewPage({Key? key}) : super(key: key);
@override
State<PageViewPage> createState() => _PageViewPageState();
}
class _PageViewPageState extends State<PageViewPage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('屏幕标题')),
body: PageView(
// 配置PageView垂直滑动,默认滑动方向是水平滑动
scrollDirection: Axis.vertical,
children: [
Center(
child: Text(
'第1屏',
style: Theme.of(context).textTheme.headlineLarge,
),
),
Center(
child: Text(
'第2屏',
style: Theme.of(context).textTheme.headlineLarge,
),
),
Center(
child: Text(
'第3屏',
style: Theme.of(context).textTheme.headlineLarge,
),
),
Center(
child: Text(
'第4屏',
style: Theme.of(context).textTheme.headlineLarge,
),
),
Center(
child: Text(
'第5屏',
style: Theme.of(context).textTheme.headlineLarge,
),
),
Center(
child: Text(
'第6屏',
style: Theme.of(context).textTheme.headlineLarge,
),
),
Center(
child: Text(
'第7屏',
style: Theme.of(context).textTheme.headlineLarge,
),
),
],
),
);
}
}
3 PageView.builder
# myflutter(项目名)目录结构如下,修改“内置的Dialog”代码
# 新增pageViewBuilder.dart,去掉dialog.dart
- pages/
- tabs/
- user.dart
- home.dart【🖊】
- setting.dart
- category.dart
- tabs.dart【🖊】
- pageViewBuilder.dart【+】
- routes/
- routes.dart【🖊】
- main.dart
3-1 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 {
final int index;
const Tabs({Key? key, this.index = 0}) : super(key: key);
@override
State<Tabs> createState() => _TabsState();
}
class _TabsState extends State<Tabs> {
late int _currentIndex;
@override
void initState() {
super.initState();
_currentIndex = widget.index;
}
final List<Widget> _pages = const [
HomePage(),
CategoryPage(),
SettingPage(),
UserPage(),
];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('PageView.builder')),
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-2 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 Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () {
Navigator.pushNamed(context, '/pageViewBuilder');
},
child: const Text('PageViewBuilder演示'),
),
],
),
);
}
}
3-3 routes.dart
import '../tabs.dart';
import '../pageViewBuilder.dart';
// 配置iOS风格的路由,引入库
import 'package:flutter/cupertino.dart';
// import 'package:flutter/material.dart';
// 定义一个Map类型的路由
Map routes = {
'/': (context) => const Tabs(),
'/pageViewBuilder': (context) => const PageViewBuilderPage(),
};
// 配置onGenerateRoute,固定写法,该方法相当于一个中间件,可以做权限判断
// ignore: prefer_function_declarations_over_variables
var onGenerateRoute = (RouteSettings settings) {
// 统一处理
final String? name = settings.name;
final Function? pageContentBuilder = routes[name];
if (pageContentBuilder != null) {
if (settings.arguments != null) {
// final Route route = MaterialPageRoute(
// 替换MaterialPageRoute
final Route route = CupertinoPageRoute(
builder:
(context) =>
pageContentBuilder(context, arguments: settings.arguments),
);
return route;
} else {
// final Route route = MaterialPageRoute(
final Route route = CupertinoPageRoute(
builder: (context) => pageContentBuilder(context),
);
return route;
}
}
return null;
};
3-4 pageViewBuilder.dart
import 'package:flutter/material.dart';
class PageViewBuilderPage extends StatefulWidget {
const PageViewBuilderPage({Key? key}) : super(key: key);
@override
State<PageViewBuilderPage> createState() => _PageViewBuilderPageState();
}
class _PageViewBuilderPageState extends State<PageViewBuilderPage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('屏幕标题')),
body: PageView.builder(
// 垂直
scrollDirection: Axis.vertical,
itemCount: 10,
itemBuilder: (context, index) {
return Center(
child: Text(
'第${index + 1}层',
style: Theme.of(context).textTheme.headlineLarge,
),
);
},
),
);
}
}
4 实现上拉无限加载
# myflutter(项目名)目录结构如下,修改“内置的Dialog”代码
# 新增pageViewFullPage.dart,去掉dialog.dart
- pages/
- tabs/
- user.dart
- home.dart【🖊】
- setting.dart
- category.dart
- tabs.dart【🖊】
- pageViewFullPage.dart【+】
- routes/
- routes.dart【🖊】
- main.dart
4-1 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 {
final int index;
const Tabs({Key? key, this.index = 0}) : super(key: key);
@override
State<Tabs> createState() => _TabsState();
}
class _TabsState extends State<Tabs> {
late int _currentIndex;
@override
void initState() {
super.initState();
_currentIndex = widget.index;
}
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: '用户'),
],
),
);
}
}
4-2 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 Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () {
Navigator.pushNamed(context, '/pageViewFullPage');
},
child: const Text('PageView上拉无限加载演示'),
),
],
),
);
}
}
4-3 routes.dart
import '../tabs.dart';
import '../pageViewFullPage.dart';
// 配置iOS风格的路由,引入库
import 'package:flutter/cupertino.dart';
// import 'package:flutter/material.dart';
// 定义一个Map类型的路由
Map routes = {
'/': (context) => const Tabs(),
'/pageViewFullPage': (context) => const PageViewFullPage(),
};
// 配置onGenerateRoute,固定写法,该方法相当于一个中间件,可以做权限判断
// ignore: prefer_function_declarations_over_variables
var onGenerateRoute = (RouteSettings settings) {
// 统一处理
final String? name = settings.name;
final Function? pageContentBuilder = routes[name];
if (pageContentBuilder != null) {
if (settings.arguments != null) {
// final Route route = MaterialPageRoute(
// 替换MaterialPageRoute
final Route route = CupertinoPageRoute(
builder:
(context) =>
pageContentBuilder(context, arguments: settings.arguments),
);
return route;
} else {
// final Route route = MaterialPageRoute(
final Route route = CupertinoPageRoute(
builder: (context) => pageContentBuilder(context),
);
return route;
}
}
return null;
};
4-4 pageViewFullPage.dart
import 'package:flutter/material.dart';
class PageViewFullPage extends StatefulWidget {
const PageViewFullPage({Key? key}) : super(key: key);
@override
State<PageViewFullPage> createState() => _PageViewFullPageState();
}
class _PageViewFullPageState extends State<PageViewFullPage> {
List<Widget> list = [];
@override
void initState() {
super.initState();
for (var i = 0; i < 10; i++) {
list.add(
Center(child: Text('第${i + 1}屏', style: const TextStyle(fontSize: 60))),
);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('屏幕标题')),
body: PageView(
scrollDirection: Axis.vertical,
onPageChanged: (index) {
print(index);
if (index + 2 == list.length) {
setState(() {
for (var i = 0; i < 10; i++) {
list.add(
Center(
child: Text(
'第${i + 1}屏',
style: const TextStyle(fontSize: 60),
),
),
);
}
});
}
},
children: list,
),
);
}
}
5 实现图片无限轮播
- Flutter中实现一个带有自动轮播功能的图片无限轮播组件,通常需要用到以下方法。
PageView.builder
构造页面列表,支持手势滑动,足够大的初始页码initialPage
来模拟无限加载。- 取模运算循环显示图片,
PageController.animateToPage
控制页面切换,配合定时器实现自动换页。 Timer.periodic
则用于定时触发页面自动切换。
5-1 实现图片手动轮播
# myflutter(项目名)目录结构如下,修改“内置的Dialog”代码
# 新增pageViewSwiper.dart、image.dart,去掉dialog.dart
- pages/
- tabs/
- user.dart
- home.dart【🖊】
- setting.dart
- category.dart
- tabs.dart【🖊】
- pageViewSwiper.dart【+】
- routes/
- routes.dart【🖊】
- widget/
- image.dart【+】
- main.dart
(1) 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 {
final int index;
const Tabs({Key? key, this.index = 0}) : super(key: key);
@override
State<Tabs> createState() => _TabsState();
}
class _TabsState extends State<Tabs> {
late int _currentIndex;
@override
void initState() {
super.initState();
_currentIndex = widget.index;
}
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: '用户'),
],
),
);
}
}
(2) 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 Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () {
Navigator.pushNamed(context, '/pageViewSwiper');
},
child: const Text('PageViewSwiper'),
),
],
),
);
}
}
(3) image.dart
import 'package:flutter/material.dart';
class ImagePage extends StatelessWidget {
final double width;
final double height;
final String src;
const ImagePage({
Key? key,
this.width = double.infinity,
this.height = 200,
required this.src,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return SizedBox(
width: width,
height: height,
child: Image.network(src, fit: BoxFit.cover),
);
}
}
(4) routes.dart
import '../tabs.dart';
import '../pageViewSwiper.dart';
// 配置iOS风格的路由,引入库
import 'package:flutter/cupertino.dart';
// import 'package:flutter/material.dart';
// 定义一个Map类型的路由
Map routes = {
'/': (context) => const Tabs(),
'/pageViewSwiper': (context) => const PageViewSwiper(),
};
// 配置onGenerateRoute,固定写法,该方法相当于一个中间件,可以做权限判断
// ignore: prefer_function_declarations_over_variables
var onGenerateRoute = (RouteSettings settings) {
// 统一处理
final String? name = settings.name;
final Function? pageContentBuilder = routes[name];
if (pageContentBuilder != null) {
if (settings.arguments != null) {
// final Route route = MaterialPageRoute(
// 替换MaterialPageRoute
final Route route = CupertinoPageRoute(
builder:
(context) =>
pageContentBuilder(context, arguments: settings.arguments),
);
return route;
} else {
// final Route route = MaterialPageRoute(
final Route route = CupertinoPageRoute(
builder: (context) => pageContentBuilder(context),
);
return route;
}
}
return null;
};
(5) pageViewSwiper.dart
import './widget/image.dart';
import 'package:flutter/material.dart';
class PageViewSwiper extends StatefulWidget {
const PageViewSwiper({Key? key}) : super(key: key);
@override
State<PageViewSwiper> createState() => _PageViewSwiperState();
}
class _PageViewSwiperState extends State<PageViewSwiper> {
List<Widget> list = [];
int _currentIndex = 0;
@override
void initState() {
super.initState();
list = const [
ImagePage(src: 'https://t.hk.uy/b8Rq'),
ImagePage(src: 'https://t.hk.uy/b8Rr'),
ImagePage(src: 'https://t.hk.uy/b8Rs'),
ImagePage(src: 'https://t.hk.uy/b8Rt'),
ImagePage(src: 'https://t.hk.uy/b8Ru'),
ImagePage(src: 'https://t.hk.uy/b8Rv'),
];
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('PageViewSwiper')),
body: Stack(
children: [
SizedBox(
height: 630,
child: PageView.builder(
onPageChanged: (index) {
// 刚开始 _currentIndex=0
setState(() {
// index=1---1 ... index=6---0
_currentIndex = index % list.length;
});
},
itemCount: 1000,
// index值为0-1000
itemBuilder: (context, index) {
return list[index % list.length];
},
),
),
Positioned(
// left和right为0,占满整行
left: 0,
right: 0,
bottom: 2,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children:
List.generate(list.length, (index) {
return Container(
margin: const EdgeInsets.all(5),
width: 10,
height: 10,
decoration: BoxDecoration(
color:
_currentIndex == index ? Colors.white : Colors.grey,
// 同borderRadius
shape: BoxShape.circle,
// borderRadius: BorderRadius.circular(5),
),
);
}).toList(),
),
),
],
),
);
}
}
5-2 Flutter实现定时器
# myflutter(项目名)目录结构如下,修改“内置的Dialog”代码
# 新增pageViewSwiper.dart、swiper.dart,去掉dialog.dart
# 创建定时器每3秒打印一次,图片手动翻页效果
- pages/
- tabs/
- user.dart
- home.dart【🖊】
- setting.dart
- category.dart
- tabs.dart【🖊】
- pageViewSwiper.dart【+】
- routes/
- routes.dart【🖊】
- widget/
- swiper.dart【+】
- main.dart
(1) 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 {
final int index;
const Tabs({Key? key, this.index = 0}) : super(key: key);
@override
State<Tabs> createState() => _TabsState();
}
class _TabsState extends State<Tabs> {
late int _currentIndex;
@override
void initState() {
super.initState();
_currentIndex = widget.index;
}
final List<Widget> _pages = const [
HomePage(),
CategoryPage(),
SettingPage(),
UserPage(),
];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Flutter实现定时器')),
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: '用户'),
],
),
);
}
}
(2) 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 Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () {
Navigator.pushNamed(context, '/pageViewSwiper');
},
child: const Text('PageViewSwiper'),
),
],
),
);
}
}
(3) routes.dart
import '../tabs.dart';
import '../pageViewSwiper.dart';
// 配置iOS风格的路由,引入库
import 'package:flutter/cupertino.dart';
// import 'package:flutter/material.dart';
// 定义一个Map类型的路由
Map routes = {
'/': (context) => const Tabs(),
'/pageViewSwiper': (context) => const PageViewSwiper(),
};
// 配置onGenerateRoute,固定写法,该方法相当于一个中间件,可以做权限判断
// ignore: prefer_function_declarations_over_variables
var onGenerateRoute = (RouteSettings settings) {
// 统一处理
final String? name = settings.name;
final Function? pageContentBuilder = routes[name];
if (pageContentBuilder != null) {
if (settings.arguments != null) {
// final Route route = MaterialPageRoute(
// 替换MaterialPageRoute
final Route route = CupertinoPageRoute(
builder:
(context) =>
pageContentBuilder(context, arguments: settings.arguments),
);
return route;
} else {
// final Route route = MaterialPageRoute(
final Route route = CupertinoPageRoute(
builder: (context) => pageContentBuilder(context),
);
return route;
}
}
return null;
};
(4) swiper.dart
import 'package:flutter/material.dart';
class Swiper extends StatefulWidget {
final double width;
final double height;
final List<String> list;
const Swiper({
Key? key,
this.width = double.infinity,
this.height = 200,
required this.list,
}) : super(key: key);
@override
State<Swiper> createState() => _SwiperState();
}
class _SwiperState extends State<Swiper> {
int _currentIndex = 0;
List<Widget> pageList = [];
@override
void initState() {
super.initState();
for (var i = 0; i < widget.list.length; i++) {
pageList.add(
ImagePage(
width: widget.width,
height: widget.height,
src: widget.list[i],
),
);
}
}
@override
Widget build(BuildContext context) {
return Stack(
children: [
SizedBox(
height: 605,
child: PageView.builder(
onPageChanged: (index) {
// 刚开始 _currentIndex=0
setState(() {
// index=1---1 ... index=6---0
_currentIndex = index % pageList.length;
});
},
itemCount: 1000,
// index值为0-1000
itemBuilder: (context, index) {
return pageList[index % pageList.length];
},
),
),
Positioned(
// left和right为0,占满整行
left: 0,
right: 0,
bottom: 2,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children:
List.generate(pageList.length, (index) {
return Container(
margin: const EdgeInsets.all(5),
width: 10,
height: 10,
decoration: BoxDecoration(
color:
_currentIndex == index ? Colors.white : Colors.grey,
// 同borderRadius
shape: BoxShape.circle,
// borderRadius: BorderRadius.circular(5),
),
);
}).toList(),
),
),
],
);
}
}
class ImagePage extends StatelessWidget {
final double width;
final double height;
final String src;
const ImagePage({
Key? key,
this.width = double.infinity,
this.height = 200,
required this.src,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return SizedBox(
width: width,
height: height,
child: Image.network(src, fit: BoxFit.cover),
);
}
}
(5) pageViewSwiper.dart
import 'dart:async';
import './widget/swiper.dart';
import 'package:flutter/material.dart';
class PageViewSwiper extends StatefulWidget {
const PageViewSwiper({Key? key}) : super(key: key);
@override
State<PageViewSwiper> createState() => _PageViewSwiperState();
}
class _PageViewSwiperState extends State<PageViewSwiper> {
// List<Widget> list=[];
List<String> list = [];
@override
void initState() {
super.initState();
list = const [
'https://t.hk.uy/b8Rq', 'https://t.hk.uy/b8Rr',
'https://t.hk.uy/b8Rs', 'https://t.hk.uy/b8Rt',
'https://t.hk.uy/b8Ru', 'https://t.hk.uy/b8Rv',
// ImagePage(src: 'https://t.hk.uy/b8Rq'),
// ImagePage(src: 'https://t.hk.uy/b8Rr'),
// ImagePage(src: 'https://t.hk.uy/b8Rs'),
// ImagePage(src: 'https://t.hk.uy/b8Rt'),
// ImagePage(src: 'https://t.hk.uy/b8Ru'),
// ImagePage(src: 'https://t.hk.uy/b8Rv'),
];
// 创建定时器,每隔3秒钟打印一句“执行”
Timer t = Timer.periodic(const Duration(seconds: 3), (timer) {
print('执行');
// timer.cancel();
});
// t.cancel();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('PageViewSwiper')),
body: ListView(children: [Swiper(list: list)]),
);
}
}
5-3 实现图片动态轮播
# myflutter(项目名)目录结构如下,修改“Flutter实现定时器”代码
- pages/
- tabs/
- user.dart
- home.dart
- setting.dart
- category.dart
- tabs.dart【🖊】
- pageViewSwiper.dart
- routes/
- routes.dart
- widget/
- swiper.dart【🖊】
- main.dart
(1) 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 {
final int index;
const Tabs({Key? key, this.index = 0}) : super(key: key);
@override
State<Tabs> createState() => _TabsState();
}
class _TabsState extends State<Tabs> {
late int _currentIndex;
@override
void initState() {
super.initState();
_currentIndex = widget.index;
}
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: '用户'),
],
),
);
}
}
(2) swiper.dart
import 'dart:async';
import 'package:flutter/material.dart';
class Swiper extends StatefulWidget {
final double width;
final double height;
final List<String> list;
const Swiper({
Key? key,
this.width = double.infinity,
this.height = 200,
required this.list,
}) : super(key: key);
@override
State<Swiper> createState() => _SwiperState();
}
class _SwiperState extends State<Swiper> {
int _currentIndex = 0;
List<Widget> pageList = [];
late PageController _pageController;
late Timer timer;
@override
void initState() {
super.initState();
// 数据
for (var i = 0; i < widget.list.length; i++) {
pageList.add(
ImagePage(
width: widget.width,
height: widget.height,
src: widget.list[i],
),
);
}
// PageController
_pageController = PageController(initialPage: 0);
timer = Timer.periodic(const Duration(seconds: 5), (t) {
// 实现图片5秒动态轮播
_pageController.animateToPage(
(_currentIndex + 1) % pageList.length,
duration: const Duration(milliseconds: 200),
curve: Curves.linear,
);
});
}
@override
void dispose() {
super.dispose();
timer.cancel();
_pageController.dispose();
}
@override
Widget build(BuildContext context) {
return Stack(
children: [
SizedBox(
height: 605,
child: PageView.builder(
controller: _pageController,
onPageChanged: (index) {
// 刚开始 _currentIndex=0
setState(() {
// index=1---1 ... index=6---0
_currentIndex = index % pageList.length;
});
},
itemCount: 1000,
// index值为0-1000
itemBuilder: (context, index) {
return pageList[index % pageList.length];
},
),
),
Positioned(
// left和right为0,占满整行
left: 0,
right: 0,
bottom: 2,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children:
List.generate(pageList.length, (index) {
return Container(
margin: const EdgeInsets.all(5),
width: 10,
height: 10,
decoration: BoxDecoration(
color:
_currentIndex == index ? Colors.white : Colors.grey,
// 同borderRadius
shape: BoxShape.circle,
// borderRadius: BorderRadius.circular(5),
),
);
}).toList(),
),
),
],
);
}
}
class ImagePage extends StatelessWidget {
final double width;
final double height;
final String src;
const ImagePage({
Key? key,
this.width = double.infinity,
this.height = 200,
required this.src,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return SizedBox(
width: width,
height: height,
child: Image.network(src, fit: BoxFit.cover),
);
}
}
6 缓存PageView页面
+