🔥 本文由 程序喵正在路上 原创,CSDN首发!
💖 系列专栏:Flutter学习
🌠 首发时间:2024年5月27日
🦋 欢迎关注🖱点赞👍收藏🌟留言🐾
目录
- Dialog组件
- AlertDialog和SimpleDialog
- 底部弹出框showModalBottomSheet
- Fluttertoast
- 自定义Dailog
- PageView组件
- PageView的使用
- PageView.builder
- PageView上拉无限加载的实现思路
- PageView实现无限轮播图
- 定时器
- 定时器加PageController实现动态轮播图
Dialog组件
AlertDialog和SimpleDialog
AlertDialog
和 SimpleDialog
都必须在 showDialog
组件中使用,而且 SimpleDialog
中的选项需要使用 SimpleDialogOption
组件来定义。
我们新建一个页面 dialog.dart 专门来演示与弹出框有关的组件,其内容如下,记得将其添加到路由中:
import 'package:flutter/material.dart';
class DialogPage extends StatefulWidget {
const DialogPage({super.key});
State<DialogPage> createState() => _DialogPageState();
}
class _DialogPageState extends State<DialogPage> {
void _alertDialog() async {
var result = await showDialog(
barrierDismissible: false, //表示点击灰色背景的时候是否消失弹出框
context: context,
builder: (context) {
return AlertDialog(
title: const Text("提示信息!"),
content: const Text("您确定要删除吗"),
actions: [
TextButton(
onPressed: () {
Navigator.of(context)
.pop("确定"); //点击按钮让AlertDialog消失, 同时返回“确定”
},
child: const Text("确定")),
TextButton(
onPressed: () {
Navigator.of(context).pop("取消");
},
child: const Text("取消"))
],
);
});
print(result);
}
void _simpleDialog() async {
var result = await showDialog(
barrierDismissible: false, //表示点击灰色背景的时候是否消失弹出框
context: context,
builder: (context) {
return SimpleDialog(
title: const Text("请选择语言"),
children: [
SimpleDialogOption(
onPressed: () {
Navigator.pop(context, "汉语");
},
child: const Text("汉语"),
),
const Divider(),
SimpleDialogOption(
onPressed: () {
Navigator.pop(context, "英语");
},
child: const Text("英语"),
),
const Divider(),
SimpleDialogOption(
onPressed: () {
Navigator.pop(context, "日语");
},
child: const Text("日语"),
),
const Divider(),
],
);
});
print(result);
}
void _modelBottomSheet() async {}
void _toast() {}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Dialog"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: _alertDialog,
child: const Text('alert弹出框-AlertDialog '),
),
const SizedBox(height: 20),
ElevatedButton(
onPressed: _simpleDialog,
child: const Text('select弹出框-SimpleDialog'),
),
const SizedBox(height: 20),
ElevatedButton(
onPressed: _modelBottomSheet,
child: const Text('ActionSheet底部弹出框'),
),
const SizedBox(height: 20),
ElevatedButton(
onPressed: _toast,
child: const Text('Toast'),
),
// fluttertoast
],
),
),
);
}
}
由于 showDialog
的返回类型为 Future
,为了接收弹出框的返回信息,我们需要在函数后面加上 async
,在 前面加上 await
,并用一个变量 result
来接收。
顺便说一下 showDialog
中的 barrierDismissible
参数,所谓的灰色背景指的是屏幕中除了弹出框以外的地方。
在 home.dart 中,我们简单写了一个按钮,可以让页面跳转到 dialog.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 Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () {
Navigator.pushNamed(context, "/dialog");
},
child: const Text("Dialog测试"),
),
],
),
);
}
}
效果:
底部弹出框showModalBottomSheet
为了减小代码篇幅,下面只给出缺少的函数。
如果你想修改底部弹出框的高度,建议在其外部套上一个 Container
或者 SizedBox
。
void _modelBottomSheet() async {
var result = await showModalBottomSheet(
context: context,
builder: (context) {
return SizedBox(
height: 240,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
ListTile(
title: const Text("分享"),
onTap: () {
Navigator.of(context).pop("分享");
},
),
const Divider(),
ListTile(
title: const Text("收藏"),
onTap: () {
Navigator.of(context).pop("收藏");
},
),
const Divider(),
ListTile(
title: const Text("取消"),
onTap: () {
Navigator.of(context).pop("取消");
},
),
const Divider(),
],
),
);
});
print(result);
}
效果:
Fluttertoast
如果你想实现屏幕上弹出一个提示信息,然后几秒后自动消失的效果,Flutter 中并没有这样的内置组件。这个时候我们可以使用一个第三方插件 —— Fluttertoast。
在 https://pub-web.flutter-io.cn/ 中搜索 fluttertoast 即可找到它,按照其使用手册即可轻松上手。
在 Readme 页面下滑,即可看到使用教程,非常简单,我们只要会复制粘贴即可:
记得引入 fluttertoast
void _toast() {
Fluttertoast.showToast(
msg: "提示信息",
toastLength: Toast.LENGTH_LONG, //提示时间,只对android生效
gravity: ToastGravity.CENTER, //出现位置
timeInSecForIosWeb: 1, //提示时间,只对ios和web生效
backgroundColor: Colors.black,
textColor: Colors.white,
fontSize: 16,
);
}
在引入第三方插件后,记得重启项目,不能热加载,效果:
自定义Dailog
自定义 Dialog
对象,需要继承 Dialog
类,尽管 Dialog
提供了 child
参数可以用来写视图界面,但是往往会达不到我们想要的效果,因为默认的 Dialog
背景框是满屏的,不符合弹出框的特点。如果我们想完全定义界面,就需要重写 build
函数。
-
新建自定义组件 myDialog.dart
InkWell
组件是 Flutter 中的一个特殊的交互式组件,它提供了水波纹效果和触摸事件处理能力。当用户点击InkWell
组件时,它会显示一个水波纹动画效果,以指示用户的交互。除了点击事件外,InkWell
组件还可以处理其他手势事件,如长按、双击等。InkWell
通常用于包装其他可点击的组件,如按钮或列表项,以增强用户交互体验。import 'package:flutter/material.dart'; class MyDialog extends Dialog { final String title; //标题 final String content; //内容 final Function()? onTap; //方便我们在外部获取其返回值 const MyDialog( {super.key, required this.title, required this.content, required this.onTap}); Widget build(BuildContext context) { return Material( type: MaterialType.transparency, //设置背景透明 child: Center( //包裹在Center组件中,不然会全屏 child: Container( height: 240, width: 240, color: Colors.white, child: Column( children: [ Padding( padding: const EdgeInsets.all(5), child: Stack( children: [ Align( alignment: Alignment.centerLeft, child: Text( title, style: const TextStyle(fontSize: 18), ), ), Align( alignment: Alignment.centerRight, child: InkWell( onTap: onTap, child: const Icon(Icons.close), ), ) ], ), ), const Divider(), Container( padding: const EdgeInsets.all(10), width: double.infinity, child: Text(content, style: const TextStyle(fontSize: 14)), ) ], ), ), ), ); } }
-
定义一个按钮,调用
MyDialog
void _myDialog() async { var result = await showDialog( context: context, builder: (context) { return MyDialog( title: "提示!", content: "我是一个内容", onTap: () { Navigator.of(context).pop("自定义Dialog关闭"); }, ); }); print(result); }
-
效果
PageView组件
Flutter 中的轮动图以及抖音上下滑页切换视频功能等等,这些都可以通过 PageView
轻松实现。
PageView
常见属性:
属性 | 描述 |
---|---|
scrollDirection | 默认为 Axis.horizontal :水平方法;Axis.vertical :垂直方向 |
children | 配置子元素 |
allowImplicitScrolling | 缓存当前页面的前后两页 |
onPageChanged | page改变的时候触发 |
PageView的使用
写一个 pageView.dart:
import 'package:flutter/material.dart';
class PageViewPage extends StatefulWidget {
const PageViewPage({super.key});
State<PageViewPage> createState() => _PageViewPageState();
}
class _PageViewPageState extends State<PageViewPage> {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("PageView"),
),
body: 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),
),
],
),
);
}
}
然后将其写在路由中,再在 home.dart 添加一个按钮用来跳转,效果就是可以上下滑动切换页面:
PageView.builder
PageView.builder
同理
import 'package:flutter/material.dart';
class PageViewBuilderPage extends StatefulWidget {
const PageViewBuilderPage({super.key});
State<PageViewBuilderPage> createState() => _PageViewBuilderPageState();
}
class _PageViewBuilderPageState extends State<PageViewBuilderPage> {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("pageViewBuilder"),
),
body: PageView.builder(
scrollDirection: Axis.vertical,
itemCount: 6, //页数
itemBuilder: (context, index) {
return Center(
child: Text("第$index屏",
style: Theme.of(context).textTheme.headlineLarge));
}),
);
}
}
PageView上拉无限加载的实现思路
新建 pageViewFullPage.dart:
import 'package:flutter/material.dart';
class PageViewFullPage extends StatefulWidget {
const PageViewFullPage({super.key});
State<PageViewFullPage> createState() => _PageViewFullPageState();
}
class _PageViewFullPageState extends State<PageViewFullPage> {
final List<Widget> _list = [];
void initState() {
super.initState();
for (var i = 0; i < 10; i++) {
_list.add(MyPage(text: "第${i + 1}屏"));
}
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("pageview无限加载"),
),
body: PageView(
scrollDirection: Axis.vertical, // 滑动方向为垂直方向
onPageChanged: (index) {
//当页面快加载完时, 继续添加页面
if (index + 2 == _list.length) {
setState(() {
for (var i = 0; i < 10; i++) {
_list.add(MyPage(text: "第${i + 1}屏"));
}
});
}
},
children: _list,
));
}
}
class MyPage extends StatefulWidget {
final String text;
const MyPage({super.key, required this.text});
State<MyPage> createState() => _MyPageState();
}
class _MyPageState extends State<MyPage> {
Widget build(BuildContext context) {
return Center(
child:
Text(widget.text, style: Theme.of(context).textTheme.headlineLarge),
);
}
}
无限加载的效果就是,当我们上滑到第10屏后,再下滑就会回到第1屏
PageView实现无限轮播图
实现如下效果,有三张图片可以左右一直滑动,同时图片下方有圆点跟随移动:
新建 pageViewSwiper.dart:
import 'package:flutter/material.dart';
class PageViewSwiperPage extends StatefulWidget {
const PageViewSwiperPage({super.key});
State<PageViewSwiperPage> createState() => _PageViewSwiperPageState();
}
class _PageViewSwiperPageState extends State<PageViewSwiperPage> {
List<Widget> list = [];
int _currentIndex = 0;
void initState() {
super.initState();
list = const [
ImagePage(
src: "https://xixi-web-tlias.oss-cn-guangzhou.aliyuncs.com/1.jpg"),
ImagePage(
src: "https://xixi-web-tlias.oss-cn-guangzhou.aliyuncs.com/2.jpg"),
ImagePage(
src: "https://xixi-web-tlias.oss-cn-guangzhou.aliyuncs.com/3.jpg"),
];
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('PageView无限轮播图'),
),
body: Stack(
children: [
SizedBox(
height: 200,
child: PageView.builder(
onPageChanged: (index) {
setState(() {
_currentIndex = index % list.length;
});
},
itemCount: 1000,
itemBuilder: (context, index) {
return list[index % list.length];
}),
),
Positioned(
left: 0,
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.blue : Colors.grey,
shape: BoxShape.circle //圆
),
);
}).toList(),
),
)
],
),
);
}
}
class ImagePage extends StatefulWidget {
final double height; //图片高度
final double width; //图片宽度
final String src; //图片地址
const ImagePage(
{super.key,
this.height = 200,
this.width = double.infinity,
required this.src});
State<ImagePage> createState() => _ImagePageState();
}
class _ImagePageState extends State<ImagePage> {
Widget build(BuildContext context) {
return SizedBox(
height: widget.height,
width: widget.width,
child: Image.network(widget.src),
);
}
}
定时器
我们可以使用 Timer.periodic()
来创建定时器,需要导包:
import 'dart:async';
//创建定时器, 3秒
var timer = Timer.periodic(const Duration(seconds: 3), (t) {
print('afterTimer=' + DateTime.now().toString());
});
组件销毁的时候取消定时器:
void dispose() {
super.dispose();
timer.cancel();
}
定时器加PageController实现动态轮播图
我们将前面实现的轮播图抽离出来,方便我们实现轮播图,同时加上定时器,让其定时切换页面,实现动态轮播图的效果。
抽离为 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(
{super.key,
this.height = 200,
this.width = double.infinity,
required this.list});
State<Swiper> createState() => _SwiperState();
}
class _SwiperState extends State<Swiper> {
int _currentIndex = 0;
List<Widget> pageList = []; //页面列表
late PageController _pageController;
late Timer timer;
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: 3), (t) {
//进行页面切换
_pageController.animateToPage((_currentIndex + 1) % pageList.length,
duration: const Duration(milliseconds: 300), curve: Curves.linear);
});
}
void dispose() {
super.dispose();
timer.cancel();
_pageController.dispose();
}
Widget build(BuildContext context) {
return Stack(
children: [
SizedBox(
height: 200,
child: PageView.builder(
controller: _pageController,
onPageChanged: (index) {
setState(() {
_currentIndex = index % pageList.length;
});
},
itemCount: 1000,
itemBuilder: (context, index) {
return pageList[index % pageList.length];
}),
),
Positioned(
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.blue : Colors.grey,
shape: BoxShape.circle //圆
),
);
}).toList(),
))
],
);
}
}
class ImagePage extends StatelessWidget {
final double width;
final double height;
final String src;
const ImagePage(
{super.key,
this.width = double.infinity,
this.height = 200,
required this.src});
Widget build(BuildContext context) {
return SizedBox(
width: width,
height: height,
child: Image.network(
src,
fit: BoxFit.cover,
),
);
}
}
pageViewSwiper.dart:
import 'package:flutter/material.dart';
import '../widget/swiper.dart';
class PageViewSwiperPage extends StatefulWidget {
const PageViewSwiperPage({super.key});
State<PageViewSwiperPage> createState() => _PageViewSwiperPageState();
}
class _PageViewSwiperPageState extends State<PageViewSwiperPage> {
List<String> list = [];
void initState() {
super.initState();
list = [
"https://xixi-web-tlias.oss-cn-guangzhou.aliyuncs.com/1.jpg",
"https://xixi-web-tlias.oss-cn-guangzhou.aliyuncs.com/2.jpg",
"https://xixi-web-tlias.oss-cn-guangzhou.aliyuncs.com/3.jpg",
"https://xixi-web-tlias.oss-cn-guangzhou.aliyuncs.com/4.jpg",
];
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('PageViewSwiper'),
),
body: ListView(
children: [
Swiper(list: list),
],
));
}
}