Flutter路由管理插件pop_scope_aware_cupertino_route的使用

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

Flutter路由管理插件pop_scope_aware_cupertino_route的使用

PopScopeAwareCupertinoRoute

PopScopeAwareCupertinoRoute是一个Flutter插件,它为CupertinoPageRoute提供了增强功能。该插件不仅可以在iOS平台上使用,而且适用于所有平台。

PopScopeAwareCupertinoRouteTransition特性

此插件的关键在于对Flutter的CupertinoPageRoute进行了修改,并集成了Cupertino Will Pop Scope,以提供以下改进:

  • 当用户尝试"滑动返回"时,提供视觉反馈——屏幕可以被拖拽一点后才会弹回原位。
  • 如果包含的路由的popDisposition设置为doNotPop,则会触发路由的onPopInvoked回调。

一个使用了此包的工作示例应用程序可以在example文件夹中找到。

插件功能展示


使用方法

要使用这个插件,在你的pubspec.yaml文件中添加pop_scope_aware_cupertino_route作为依赖项。

示例代码

导入库

// main.dart
import 'package:pop_scope_aware_cupertino_route/pop_scope_aware_cupertino_route.dart';

配置页面过渡效果

在主题配置中将所需平台的过渡构建器设置为PopScopeAwareCupertinoPageTransitionBuilder

// material_app.dart
final theme = ThemeData(
  pageTransitionsTheme: const PageTransitionsTheme(
    builders: {
      TargetPlatform.android: ZoomPageTransitionsBuilder(),
      TargetPlatform.iOS: PopScopeAwareCupertinoPageTransitionBuilder(),
    },
  ),
);

确保将主题应用到应用程序中。

// material_app.dart
@override
Widget build(BuildContext context) {
  return MaterialApp(
    theme: theme,
    home: const HomeScreen(),
  );
}

使用PopScope

// my_screen.dart
@override
Widget build(BuildContext context) {
  return PopScope(
    canPop: false, // 是否允许返回
    onPopInvoked: (canPop) {
      print(canPop); // 返回是否被允许
    },
    child: Container(), // 页面内容
  );
}

完整示例Demo

下面是一个完整的示例,演示如何使用pop_scope_aware_cupertino_route创建两个屏幕之间的导航,并控制返回行为。

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:pop_scope_aware_cupertino_route/pop_scope_aware_cupertino_route.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData(
        pageTransitionsTheme: const PageTransitionsTheme(
          builders: {
            TargetPlatform.android: ZoomPageTransitionsBuilder(),
            TargetPlatform.iOS: PopScopeAwareCupertinoPageTransitionBuilder(),
          },
        ),
      ),
      home: const HomeScreen(),
    );
  }
}

class HomeScreen extends StatefulWidget {
  const HomeScreen({super.key});

  @override
  State<HomeScreen> createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> {
  Future<String?> _goToSecondScreen(BuildContext context) =>
      Navigator.of(context).push<String>(
          MaterialPageRoute(builder: (_) => const SecondScreen()));

  String? result;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Demo')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          crossAxisAlignment: CrossAxisAlignment.center,
          children: [
            Text('Result: $result'),
            ElevatedButton(
              onPressed: () async {
                final newResult = await _goToSecondScreen(context);
                setState(() {
                  result = newResult;
                });
              },
              child: const Padding(
                padding: EdgeInsets.symmetric(horizontal: 24.0, vertical: 12.0),
                child: Text('Go to second screen'),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

class SecondScreen extends StatefulWidget {
  const SecondScreen({super.key});

  @override
  State<SecondScreen> createState() => _SecondScreenState();
}

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

  _onWillPop(bool popAllowed, result) async {
    if (!popAllowed) {
      showAdaptiveDialog(
        context: context,
        barrierDismissible: true,
        builder: (context) => AlertDialog.adaptive(
          content: const Text('Back navigation is disabled.'),
          actions: [
            adaptiveAction(
              context: context,
              child: const Text('pop till home'),
              onPressed: () => Navigator.of(context)
                ..pop()
                ..pop('popped in dialog'),
            ),
            adaptiveAction(
              context: context,
              child: const Text('OK'),
              onPressed: Navigator.of(context).pop,
            ),
          ],
        ),
      );
    }
  }

  Widget adaptiveAction({
    required BuildContext context,
    required VoidCallback onPressed,
    required Widget child,
  }) {
    final ThemeData theme = Theme.of(context);
    switch (theme.platform) {
      case TargetPlatform.android:
      case TargetPlatform.fuchsia:
      case TargetPlatform.linux:
      case TargetPlatform.windows:
        return TextButton(onPressed: onPressed, child: child);
      case TargetPlatform.iOS:
      case TargetPlatform.macOS:
        return CupertinoDialogAction(onPressed: onPressed, child: child);
    }
  }

  void _updateChanges(bool value) => setState(() => _popAllowed = value);

  @override
  Widget build(BuildContext context) {
    return PopScope(
      canPop: _popAllowed,
      onPopInvokedWithResult: _onWillPop,
      child: Scaffold(
        appBar: AppBar(title: const Text('Second Screen')),
        body: Column(
          children: [
            SwitchListTile(
              title: const Text('Back Navigation Enabled'),
              value: _popAllowed,
              onChanged: _updateChanges,
            ),
            ListTile(
              title: const Text('pop till home with result'),
              onTap: () => Navigator.of(context).pop('popped till home'),
            )
          ],
        ),
      ),
    );
  }
}

在这个例子中,我们创建了一个简单的Flutter应用,其中包含两个屏幕:HomeScreenSecondScreen。通过PopScope组件来控制是否允许从SecondScreen返回到HomeScreen,并且当返回操作被禁止时,会显示一个对话框给用户提示。此外,还展示了如何根据不同的平台(Android或iOS)自适应地选择不同的对话框样式。


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

1 回复

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


当然,pop_scope_aware_cupertino_route 是一个用于 Flutter 的路由管理插件,它扩展了 Cupertino 风格的路由,使其支持 WillPopScope 功能,这在 iOS 风格的应用中特别有用。当你需要在用户尝试返回上一页时执行某些操作时,这个插件会非常有用。

以下是一个如何使用 pop_scope_aware_cupertino_route 的代码示例:

1. 添加依赖

首先,在你的 pubspec.yaml 文件中添加 pop_scope_aware_cupertino_route 依赖:

dependencies:
  flutter:
    sdk: flutter
  pop_scope_aware_cupertino_route: ^最新版本号 # 请替换为实际最新版本号

然后运行 flutter pub get 来获取依赖。

2. 导入并使用插件

接下来,在你的 Flutter 应用中导入并使用这个插件。假设你有一个简单的应用,有两个页面:HomePageSecondPage

主文件 (main.dart)

import 'package:flutter/material.dart';
import 'package:pop_scope_aware_cupertino_route/pop_scope_aware_cupertino_route.dart';
import 'package:flutter/cupertino.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return CupertinoApp(
      title: 'Flutter Demo',
      home: HomePage(),
      routes: {
        '/second': (context) => SecondPage(),
      },
      navigatorObservers: [
        RouteObserver<CupertinoPageRoute<dynamic>>(), // 添加一个 RouteObserver
      ],
    );
  }
}

class HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return CupertinoPageScaffold(
      navigationBar: CupertinoNavigationBar(
        middle: Text('Home Page'),
        trailing: CupertinoNavigationBar.trailing(
          text: 'Next',
          onPressed: () {
            Navigator.of(context).pushNamed('/second');
          },
        ),
      ),
      child: Center(
        child: Text('This is the Home Page'),
      ),
    );
  }
}

class SecondPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return CupertinoPageScaffold(
      navigationBar: CupertinoNavigationBar(
        middle: Text('Second Page'),
      ),
      child: WillPopScope(
        onWillPop: () async {
          // 在这里处理返回操作
          bool? shouldPop = await showCupertinoDialog(
            context: context,
            builder: (context) {
              return CupertinoAlertDialog(
                title: Text('Are you sure?'),
                content: Text('Do you want to go back?'),
                actions: [
                  CupertinoDialogAction(
                    isDefaultAction: true,
                    child: Text('Cancel'),
                    onPressed: () => Navigator.pop(context, false),
                  ),
                  CupertinoDialogAction(
                    child: Text('OK'),
                    onPressed: () => Navigator.pop(context, true),
                  ),
                ],
              );
            },
          );
          return shouldPop ?? false;
        },
        child: CupertinoPageScaffold(
          child: Center(
            child: Text('This is the Second Page'),
          ),
        ),
      ),
    );
  }
}

// 自定义 CupertinoPageRoute 以支持 WillPopScope
class PopScopeAwareCupertinoRoute<T> extends PopScopeAwareRoute<T> {
  PopScopeAwareCupertinoRoute({
    required WidgetBuilder builder,
    RouteSettings? settings,
  }) : super(builder: builder, settings: settings);

  @override
  Widget buildPage(BuildContext context, _, __) {
    final CupertinoPageScaffold scaffold = CupertinoPageScaffold(
      child: builder!(context),
    );
    return scaffold;
  }

  @override
  CupertinoPageRoute<T> getCupertinoPageRoute() {
    return CupertinoPageRoute<T>(
      builder: builder,
      settings: settings,
    );
  }
}

// 自定义 RouterDelegate 和 CupertinoRouterDelegate
class MyRouterDelegate extends RouterDelegate<CupertinoRoute>
    with ChangeNotifier, PopNavigatorRouterDelegateMixin<CupertinoRoute> {
  @override
  final List<NavigatorObserver> navigatorObservers = [];

  @override
  Widget build(BuildContext context) {
    return CupertinoTabScaffold(
      tabBar: CupertinoTabBar(
        items: [
          BottomNavigationBarItem(
            icon: Icon(CupertinoIcons.home),
            label: 'Home',
          ),
          BottomNavigationBarItem(
            icon: Icon(CupertinoIcons.search),
            label: 'Search',
          ),
        ],
      ),
      tabBuilder: (context, index) {
        switch (index) {
          case 0:
            return Navigator(
              onGenerateRoute: (settings) {
                if (settings.name == '/') {
                  return PopScopeAwareCupertinoRoute(
                    builder: (context) => HomePage(),
                  );
                }
                return null;
              },
              observers: navigatorObservers,
            );
          case 1:
            return Navigator(
              onGenerateRoute: (settings) {
                if (settings.name == '/search') {
                  // 这里可以添加 SearchPage 的路由
                  return null;
                }
                return null;
              },
              observers: navigatorObservers,
            );
          default:
            return Container();
        }
      },
    );
  }

  @override
  CupertinoRoute? currentRouteAfterPop() => null;
}

// 在 MyApp 中使用自定义 RouterDelegate
class MyAppWithRouter extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return CupertinoApp(
      title: 'Flutter Demo',
      onGenerateRouter: (_) => MyRouterDelegate(),
    );
  }
}

注意:

  1. PopScopeAwareCupertinoRoute 类扩展了 PopScopeAwareRoute 并实现了 CupertinoPageRoute 的构建。
  2. MyApp 中,你可以使用自定义的 RouterDelegate(如 MyRouterDelegate)来管理路由,但这在上面的示例中未完全展示,仅展示了基本的 CupertinoApp 结构。
  3. SecondPage 中,WillPopScope 用于处理返回按钮的点击事件,显示一个确认对话框。

在实际项目中,你可能需要根据你的应用结构调整代码。这个示例提供了如何使用 pop_scope_aware_cupertino_route 的基本框架。

回到顶部