flutter3.0学习笔记

WillPopScope

Preview

为了避免用户误触返回按钮而导致 App 退出,在很多 App 中都拦截了用户点击返回键的按钮,然后进行一些防误触判断,比如当用户在某一个时间段内点击两次时,才会认为用户是要退出(而非误触)。比如禁止 Android 系统后退按钮、询问用户是否退等,我们可以包裹一层WillPopScope并设置onWillPop参数。构造函数:

const WillPopScope({
    super.key,
    required this.child,
    required this.onWillPop,//回调函数,当用户点击返回按钮时被调用
  })
  • 用法
@override
Widget build(BuildContext context) {
    WillPopScope(
      onWillPop: onWillPop,
      child: Scaffold(
        //界面内容
      );
    )
)
 
onWillPop: (){
  print('即将退出本界面');
  //return Future.value(false);//阻止退出
  return Future.value(true);//退出 
}
  • 禁止 Android 系统后退按钮
import 'package:flutter/material.dart';

class WillPopScopePage extends StatelessWidget {
  const WillPopScopePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      title: 'WillPopScope',
      home: HomePage(),
    );
  }
}

class HomePage extends StatelessWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(centerTitle: true,title: const Text('WillPopScope'),),
      body: Center(
        child: ElevatedButton(
          child: const Text('Go to OtherPage'),
          onPressed: (){
            Navigator.of(context).push(
              MaterialPageRoute(builder: (context)=>const OtherPage())
            );
          },
        ),
      ),
    );
  }
}

// OtherPage
class OtherPage extends StatelessWidget {
  const OtherPage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return WillPopScope(
      onWillPop: () async {
        //弹出
        ScaffoldMessenger.of(context).showSnackBar(
          const SnackBar(content: Text('系统后退按钮已停用'))
        );
        return false;
      },
      child: Scaffold(
        appBar: AppBar(title: const Text('OtherPage'),centerTitle: true,),
        body: const Padding(
          padding: EdgeInsets.all(30),
          child: Text('禁止退出'),
        ),
      )
    );
  }
}

image.png

  • 询问用户是否退出
import 'package:flutter/material.dart';

class WillPopScopePage extends StatefulWidget {
  const WillPopScopePage({Key? key}) : super(key: key);

  @override
  State<WillPopScopePage> createState() => _WillPopScopePageState();
}

class _WillPopScopePageState extends State<WillPopScopePage> {
  Future<bool?> _onBackPressed(BuildContext context) async{
    return showDialog<bool>(
        context: context,
        builder: (BuildContext context){
          return  AlertDialog(
            title: const Text('确定要退出吗?'),
            actions: [
              TextButton(
                  onPressed: (){
                    Navigator.of(context).pop(false);
                  },
                  child: const Text('取消')
              ),
              TextButton(
                  onPressed: (){
                    Navigator.of(context).pop(true);
                  },
                  child: const Text('确定')
              )
            ],
          );
        }
    );
  }

  @override
  Widget build(BuildContext context) {
    return WillPopScope(
      onWillPop: () async{
        bool? result = await _onBackPressed(context);
        result ??= false; //判断是否为null,是就赋值false
        return result;
      },
      child: Scaffold(
        //key: _scaffoldkey,
        body: SafeArea(
          child: SingleChildScrollView(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                Stack(
                  children: [
                    Positioned(
                        child: AppBar(
                          centerTitle: true,
                          title:const Text(
                            'Sample',
                            style: TextStyle(
                              fontWeight: FontWeight.bold,
                              fontSize: 20,
                              color: Colors.black87,
                            ),
                          ),
                          backgroundColor: Colors.transparent,
                          elevation: 0,
                          leading: IconButton(
                            icon: const Icon(Icons.arrow_back,color: Colors.black,size: 30,),
                            onPressed: (){_onBackPressed(context);},
                          ),
                        )
                    )
                  ],
                )
              ],
            ),
          ),
        ),
      ),
    );
  }
}

Screenshot_20221113_162029.png

  • 防止 Android 用户误触返回键退出,我们拦截返回事件。
import 'package:flutter/material.dart';
class WillPopScopePage extends StatefulWidget {
  const WillPopScopePage({Key? key}) : super(key: key);

  @override
  State<WillPopScopePage> createState() => _WillPopScopePageState();
}

class _WillPopScopePageState extends State<WillPopScopePage> {
  DateTime? _lastPressedAt; //上次点击时间
  @override
  Widget build(BuildContext context) {
    return WillPopScope(
      onWillPop: () async {
        if(_lastPressedAt == null || DateTime.now().difference(_lastPressedAt!)>Duration(seconds: 1)){
          //两次点击间隔超过1秒则重新计时
          _lastPressedAt = DateTime.now();
          return false;
        }
        return true;
      },
      child: Scaffold(
        appBar: AppBar(title: const Text('连续点击退出'),),
        body: Container(
          alignment: Alignment.center,
          child: const Text('1秒内连续两次按返回按钮则退出'),
        ),
      )
    );
  }
}