【Flutter】Dialog组件&PageView组件

🔥 本文由 程序喵正在路上 原创,CSDN首发!
💖 系列专栏:Flutter学习
🌠 首发时间:2024年5月27日
🦋 欢迎关注🖱点赞👍收藏🌟留言🐾

Dialog组件

AlertDialog和SimpleDialog

AlertDialogSimpleDialog 都必须在 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 函数。

  1. 新建自定义组件 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)),
                  )
                ],
              ),
            ),
          ),
        );
      }
    }
    
  2. 定义一个按钮,调用 MyDialog

    void _myDialog() async {
      var result = await showDialog(
          context: context,
          builder: (context) {
            return MyDialog(
              title: "提示!",
              content: "我是一个内容",
              onTap: () {
                Navigator.of(context).pop("自定义Dialog关闭");
              },
            );
          });
      print(result);
    }
    
  3. 效果

    在这里插入图片描述

PageView组件

Flutter 中的轮动图以及抖音上下滑页切换视频功能等等,这些都可以通过 PageView 轻松实现。

PageView 常见属性:

属性描述
scrollDirection默认为 Axis.horizontal:水平方法;Axis.vertical:垂直方向
children配置子元素
allowImplicitScrolling缓存当前页面的前后两页
onPageChangedpage改变的时候触发

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),
          ],
        ));
  }
}

在这里插入图片描述
在这里插入图片描述

  • 13
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序喵正在路上

你的鼓励是我创作最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值