AsyncRedux导航Skill asyncredux-navigation

AsyncRedux导航技能是一种在Flutter应用中使用AsyncRedux库通过分发动作来处理导航的方法,旨在简化导航逻辑的单元测试。它支持多种导航操作如push、pop、replace等,并允许从动作内部触发导航。关键词包括:AsyncRedux、Flutter、导航、动作分发、单元测试、NavigateAction、移动开发、前端开发。

移动开发 0 次安装 0 次浏览 更新于 3/19/2026

name: asyncredux-navigation description: 通过使用NavigateAction处理导航。涵盖设置导航器键、分发NavigateAction进行push/pop/replace,以及隔离测试导航。

使用NavigateAction进行导航

AsyncRedux允许通过分发动作进行应用导航,使导航逻辑更容易单元测试。此方法是可选的,目前仅支持Navigator 1。

设置

1. 创建和注册导航器键

创建一个全局导航器键,并在应用初始化期间用NavigateAction注册它:

import 'package:async_redux/async_redux.dart';
import 'package:flutter/material.dart';

final navigatorKey = GlobalKey<NavigatorState>();

void main() async {
  NavigateAction.setNavigatorKey(navigatorKey);
  // ... 其余初始化
  runApp(MyApp());
}

2. 配置MaterialApp

将相同的导航器键传递给您的MaterialApp:

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return StoreProvider<AppState>(
      store: store,
      child: MaterialApp(
        routes: {
          '/': (context) => HomePage(),
          '/details': (context) => DetailsPage(),
          '/settings': (context) => SettingsPage(),
        },
        navigatorKey: navigatorKey,
      ),
    );
  }
}

分发导航动作

Push操作

// 推送一个命名路由
dispatch(NavigateAction.pushNamed('/details'));

// 推送一个路由对象
dispatch(NavigateAction.push(
  MaterialPageRoute(builder: (context) => DetailsPage()),
));

// 推送并替换当前路由(命名)
dispatch(NavigateAction.pushReplacementNamed('/newRoute'));

// 推送并替换当前路由(使用路由对象)
dispatch(NavigateAction.pushReplacement(
  MaterialPageRoute(builder: (context) => NewPage()),
));

// 弹出当前路由并推送一个新的命名路由
dispatch(NavigateAction.popAndPushNamed('/otherRoute'));

// 推送命名路由并移除所有路由直到谓词为真
dispatch(NavigateAction.pushNamedAndRemoveUntil(
  '/home',
  (route) => false, // 移除所有路由
));

// 推送命名路由并移除所有路由(便捷方法)
dispatch(NavigateAction.pushNamedAndRemoveAll('/home'));

// 推送路由并移除直到谓词
dispatch(NavigateAction.pushAndRemoveUntil(
  MaterialPageRoute(builder: (context) => HomePage()),
  (route) => false,
));

Pop操作

// 弹出当前路由
dispatch(NavigateAction.pop());

// 弹出并返回一个结果值
dispatch(NavigateAction.pop(result: 'some_value'));

// 弹出路由直到谓词为真
dispatch(NavigateAction.popUntil((route) => route.isFirst));

// 弹出直到达到特定命名路由
dispatch(NavigateAction.popUntilRouteName('/home'));

// 弹出直到达到特定路由
dispatch(NavigateAction.popUntilRoute(someRoute));

Replace操作

// 用新路由替换特定路由
dispatch(NavigateAction.replace(
  oldRoute: currentRoute,
  newRoute: MaterialPageRoute(builder: (context) => NewPage()),
));

// 替换当前路由下方的路由
dispatch(NavigateAction.replaceRouteBelow(
  anchorRoute: currentRoute,
  newRoute: MaterialPageRoute(builder: (context) => NewPage()),
));

Remove操作

// 移除特定路由
dispatch(NavigateAction.removeRoute(routeToRemove));

// 移除特定路由下方的路由
dispatch(NavigateAction.removeRouteBelow(anchorRoute));

完整示例

import 'package:async_redux/async_redux.dart';
import 'package:flutter/material.dart';

late Store<AppState> store;
final navigatorKey = GlobalKey<NavigatorState>();

void main() async {
  NavigateAction.setNavigatorKey(navigatorKey);
  store = Store<AppState>(initialState: AppState());
  runApp(MyApp());
}

class AppState {}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return StoreProvider<AppState>(
      store: store,
      child: MaterialApp(
        routes: {
          '/': (context) => HomePage(),
          '/details': (context) => DetailsPage(),
        },
        navigatorKey: navigatorKey,
      ),
    );
  }
}

class HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('首页')),
      body: Center(
        child: ElevatedButton(
          child: Text('前往详情'),
          onPressed: () => context.dispatch(NavigateAction.pushNamed('/details')),
        ),
      ),
    );
  }
}

class DetailsPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('详情')),
      body: Center(
        child: ElevatedButton(
          child: Text('返回'),
          onPressed: () => context.dispatch(NavigateAction.pop()),
        ),
      ),
    );
  }
}

获取当前路由名称

无需在应用状态中存储当前路由(这可能会造成复杂性),直接访问:

String routeName = NavigateAction.getCurrentNavigatorRouteName(context);

从动作中导航

您可以从其他动作内部分发导航动作:

class LoginAction extends ReduxAction<AppState> {
  final String username;
  final String password;

  LoginAction({required this.username, required this.password});

  @override
  Future<AppState?> reduce() async {
    final user = await api.login(username, password);

    // 成功登录后导航到首页
    dispatch(NavigateAction.pushReplacementNamed('/home'));

    return state.copy(user: user);
  }
}

测试导航

NavigateAction允许在不使用小部件或驱动测试的情况下进行导航单元测试:

test('登录成功后导航到首页', () async {
  final store = Store<AppState>(initialState: AppState());

  // 捕获分发的动作
  NavigateAction? navigateAction;
  store.actionObservers.add((action, ini, prevState, newState) {
    if (action is NavigateAction) {
      navigateAction = action;
    }
  });

  await store.dispatchAndWait(LoginAction(
    username: 'test',
    password: 'password',
  ));

  // 断言导航类型
  expect(navigateAction!.type, NavigateType.pushReplacementNamed);

  // 断言路由名称
  expect(
    (navigateAction!.details as NavigatorDetails_PushReplacementNamed).routeName,
    '/home',
  );
});

NavigateType枚举值

NavigateType枚举包括所有导航操作的值:

  • push, pushNamed
  • pop
  • pushReplacement, pushReplacementNamed
  • popAndPushNamed
  • pushAndRemoveUntil, pushNamedAndRemoveUntil, pushNamedAndRemoveAll
  • popUntil, popUntilRouteName, popUntilRoute
  • replace, replaceRouteBelow
  • removeRoute, removeRouteBelow

重要注意事项

  • 通过AsyncRedux进行导航完全可选
  • 目前仅支持Navigator 1
  • 对于现代导航包(如go_router),您需要创建自定义动作实现
  • 不要将当前路由存储在应用状态中;使用getCurrentNavigatorRouteName()代替

参考文献

文档中的URL: