Flutter路由返回拦截插件custom_will_pop_scope的使用

发布于 1周前 作者 htzhanglong 来自 Flutter

Flutter路由返回拦截插件custom_will_pop_scope的使用

简介

custom_will_pop_scope 是一个用于在 Flutter 中拦截路由返回的插件。它可以让你在用户尝试返回上一个页面时执行自定义逻辑,例如显示确认对话框或阻止返回操作。

安装

要使用此插件,需要在 pubspec.yaml 文件中添加依赖:

dependencies:
  custom_will_pop_scope: ^最新版本号

示例代码

以下是一个完整的示例项目,展示了如何使用 custom_will_pop_scope 插件。

1. 导入库

首先,在你的 Dart 文件中导入 custom_will_pop_scope 库:

import 'package:custom_will_pop_scope/custom_will_pop_scope.dart';

2. 设置导航服务

为了更好地管理导航,我们使用一个简单的导航服务类。创建一个 navigation_service.dart 文件:

import 'package:flutter/material.dart';

class NavigationService {
  final GlobalKey<NavigatorState> navigationKey = GlobalKey<NavigatorState>();

  Future<dynamic> navigateTo(String routeName) {
    return navigationKey.currentState!.pushNamed(routeName);
  }

  void removeLastRouteName() {
    // 这里可以实现你自己的逻辑,例如记录路由历史等
  }
}

3. 设置依赖注入

创建一个 locator.dart 文件来设置依赖注入:

import 'package:get_it/get_it.dart';
import 'navigation_service.dart';

final locator = GetIt.instance;

void setupLocator() {
  locator.registerLazySingleton(() => NavigationService());
}

4. 创建路由配置

创建一个 router.dart 文件来配置路由:

const String homeScreen = '/';
const String secondScreen = '/second';

class Routes {
  static Route<dynamic> generateRoute(RouteSettings settings) {
    switch (settings.name) {
      case homeScreen:
        return MaterialPageRoute(builder: (_) => HomeScreen());
      case secondScreen:
        return MaterialPageRoute(builder: (_) => SecondScreen());
      default:
        return MaterialPageRoute(
          builder: (_) => Scaffold(
            body: Center(
              child: Text('No route defined for ${settings.name}'),
            ),
          ),
        );
    }
  }
}

5. 主应用入口

创建 main.dart 文件作为主应用入口:

import 'package:custom_will_pop_scope/custom_will_pop_scope.dart';
import 'package:example/locator.dart';
import 'package:example/navigation_service.dart';
import 'package:example/router.dart';
import 'package:flutter/material.dart';

void main() {
  setupLocator();
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  final theme = ThemeData(
    primarySwatch: Colors.blue,
  );

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: theme,
      home: HomeScreen(),
      initialRoute: homeScreen,
      onGenerateRoute: Routes.generateRoute,
      navigatorKey: locator<NavigationService>().navigationKey,
    );
  }
}

class HomeScreen extends StatelessWidget {
  void navigateToNext() {
    locator<NavigationService>().navigateTo(secondScreen);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('CustomWillPopScope Demo')),
      body: Center(
        child: ElevatedButton(
          child: Padding(
            padding: EdgeInsets.symmetric(horizontal: 24.0, vertical: 12.0),
            child: Text('Go to second screen'),
          ),
          onPressed: () => navigateToNext(),
        ),
      ),
    );
  }
}

class SecondScreen extends StatefulWidget {
  @override
  _SecondScreenState createState() => _SecondScreenState();
}

class _SecondScreenState extends State<SecondScreen> {
  final Color _color = Colors.pink;
  final NavigationService _navigationService = locator<NavigationService>();

  /// 持有屏幕的状态
  bool _canReturn = true;

  /// 显示警告并返回 `false` 当 `_canReturn` 为 `true`
  /// 这将阻止导航器弹出此路由
  Future<bool> _onWillPop() async {
    if (!_canReturn) {
      // 显示警告对话框
      showDialog(
        context: context,
        builder: (context) => AlertDialog(
          content: Text('Back navigation is disabled.'),
        ),
      );
      // 返回 `false` 以防止路由弹出
      return false;
    } else {
      _navigationService.removeLastRouteName();
      return true;
    }
  }

  void _onPopAction() {
    _navigationService.removeLastRouteName();
  }

  /// 更新 `_canReturn` 的值
  void _updateChanges(bool value) => setState(() => _canReturn = value);

  @override
  Widget build(BuildContext context) {
    return CustomWillPopScope(
      // canReturn 是可选的,默认为 true
      canReturn: _canReturn,
      onWillPop: _onWillPop,
      // onPopAction 是可选的,当关闭 [ModalRoute] 时调用
      onPopAction: _onPopAction,
      child: Scaffold(
        appBar: AppBar(title: Text('Second Screen'), backgroundColor: _color),
        body: Column(
          children: [
            Container(
              child: SwitchListTile(
                activeColor: _color,
                onChanged: _updateChanges,
                title: Text('Enable back navigation'),
                value: _canReturn,
              ),
              decoration: BoxDecoration(
                border: Border.all(color: _color),
                borderRadius: BorderRadius.circular(6.0),
              ),
              margin: EdgeInsets.symmetric(horizontal: 12.0),
            ),
            Expanded(
              child: PageView(
                children: [
                  Scaffold(
                    body: Center(
                      child: Text("Page 1"),
                    ),
                  ),
                  Scaffold(
                    body: Center(
                      child: Text("Page 2"),
                    ),
                  ),
                  Scaffold(
                    body: Center(
                      child: Text("Page 3"),
                    ),
                  ),
                ],
              ),
            ),
          ],
        ),
      ),
    );
  }
}

使用说明

  1. 导入库:在需要使用 CustomWillPopScope 的文件中导入 custom_will_pop_scope 库。
  2. 包裹屏幕:使用 CustomWillPopScope 包裹需要拦截返回操作的屏幕,并定义 onWillPop 回调函数。
  3. 设置返回条件:通过 canReturn 属性控制是否允许返回。如果 canReturnfalse,则会调用 onWillPop 回调函数。
  4. 处理返回操作:在 onWillPop 回调函数中,你可以显示对话框或其他逻辑来阻止返回操作。如果返回 false,则阻止返回;如果返回 true,则允许返回。

注意事项

  • onWillPop 回调函数必须返回一个 Future<bool>
  • canReturn 属性是可选的,默认为 true
  • onPopAction 属性也是可选的,用于在关闭 ModalRoute 时调用某个函数。

通过以上步骤,你可以在 Flutter 应用中轻松地实现路由返回拦截功能。希望这个示例对你有所帮助!


更多关于Flutter路由返回拦截插件custom_will_pop_scope的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter路由返回拦截插件custom_will_pop_scope的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


在Flutter中,custom_will_pop_scope 插件允许你拦截路由返回(即用户点击返回按钮或执行 Navigator.pop 操作)的事件,并在拦截时执行自定义逻辑。虽然Flutter本身没有直接名为 custom_will_pop_scope 的插件,但类似的功能可以通过 WillPopScope 小部件来实现。

下面是一个如何使用 WillPopScope 来实现路由返回拦截的示例代码:

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      initialRoute: '/',
      routes: {
        '/': (context) => FirstScreen(),
        '/second': (context) => SecondScreen(),
      },
    );
  }
}

class FirstScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('First Screen'),
      ),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            Navigator.pushNamed(context, '/second');
          },
          child: Text('Go to Second Screen'),
        ),
      ),
    );
  }
}

class SecondScreen extends StatefulWidget {
  @override
  _SecondScreenState createState() => _SecondScreenState();
}

class _SecondScreenState extends State<SecondScreen> {
  bool _hasUnsavedChanges = false;

  @override
  Widget build(BuildContext context) {
    return WillPopScope(
      onWillPop: () async {
        if (_hasUnsavedChanges) {
          // 显示对话框询问用户是否真的要离开
          final result = await showDialog<bool>(
            context: context,
            builder: (context) {
              return AlertDialog(
                title: Text('Do you really want to exit?'),
                content: Text('You have unsaved changes.'),
                actions: <Widget>[
                  TextButton(
                    onPressed: () => Navigator.pop(context, false),
                    child: Text('Cancel'),
                  ),
                  TextButton(
                    onPressed: () => Navigator.pop(context, true),
                    child: Text('Yes'),
                  ),
                ],
              );
            },
          );
          return result ?? false;
        }
        return true; // 如果没有未保存的更改,允许返回
      },
      child: Scaffold(
        appBar: AppBar(
          title: Text('Second Screen'),
        ),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              ElevatedButton(
                onPressed: () {
                  setState(() {
                    _hasUnsavedChanges = true;
                  });
                },
                child: Text('Create Unsaved Changes'),
              ),
              ElevatedButton(
                onPressed: () {
                  setState(() {
                    _hasUnsavedChanges = false;
                  });
                },
                child: Text('Save Changes'),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

代码解释:

  1. MyApp

    • 定义了一个简单的 MaterialApp,其中包含了两个路由://second
    • / 路由指向 FirstScreen/second 路由指向 SecondScreen
  2. FirstScreen

    • 这是一个简单的屏幕,包含一个按钮,点击按钮会导航到 SecondScreen
  3. SecondScreen

    • 使用 StatefulWidget 来管理未保存更改的状态 _hasUnsavedChanges
    • 使用 WillPopScope 小部件来拦截返回操作。
    • onWillPop 方法会在用户尝试返回时被调用。如果 _hasUnsavedChangestrue,则会显示一个对话框询问用户是否真的要离开。
    • 用户可以选择“Cancel”保留在当前页面,或选择“Yes”返回上一个页面。

这样,你就可以在Flutter应用中实现路由返回的拦截,并根据需要执行自定义逻辑。

回到顶部