在这里插入图片描述

前言

导航与路由管理是应用架构的核心组成部分,它决定了用户如何在不同页面之间切换以及数据如何在页面间传递。在笔记应用中,从列表页到详情页、从详情页到编辑页、从设置页返回主页等场景都需要使用导航功能。一个设计良好的路由系统应该支持页面跳转、参数传递、返回结果等功能。本文将详细介绍如何在Flutter和OpenHarmony平台上实现应用内导航与路由管理。

Flutter基础导航

Flutter使用Navigator进行页面导航。

class NotesListPage extends StatelessWidget {
  void _openNoteDetail(BuildContext context, Note note) {
    Navigator.push(
      context,
      MaterialPageRoute(
        builder: (context) => NoteDetailPage(note: note),
      ),
    );
  }
  
  
  Widget build(BuildContext context) {
    return ListView.builder(
      itemCount: notes.length,
      itemBuilder: (context, index) {
        return ListTile(
          title: Text(notes[index].title),
          onTap: () => _openNoteDetail(context, notes[index]),
        );
      },
    );
  }
}

Navigator.push将新页面压入导航栈,MaterialPageRoute提供Material风格的页面过渡动画。builder回调返回目标页面的Widget,可以通过构造函数传递参数。这是Flutter中最基本的页面跳转方式,适合简单的导航场景。

void _openNoteDetailAndWaitResult(BuildContext context, Note note) async {
  final result = await Navigator.push<bool>(
    context,
    MaterialPageRoute(
      builder: (context) => NoteDetailPage(note: note),
    ),
  );
  
  if (result == true) {
    _refreshNotesList();
  }
}

// 在NoteDetailPage中返回结果
void _deleteNote() {
  // 删除逻辑...
  Navigator.pop(context, true);
}

Navigator.push可以等待目标页面返回结果。泛型参数指定返回值类型,await等待页面关闭。目标页面通过Navigator.pop的第二个参数返回结果。这种模式常用于编辑页面,返回后刷新列表数据。

命名路由

命名路由使用字符串标识页面,便于集中管理。

class MyApp extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return MaterialApp(
      initialRoute: '/',
      routes: {
        '/': (context) => NotesListPage(),
        '/note/detail': (context) => NoteDetailPage(),
        '/note/edit': (context) => NoteEditPage(),
        '/settings': (context) => SettingsPage(),
      },
    );
  }
}

// 使用命名路由导航
Navigator.pushNamed(context, '/note/detail', arguments: note);

// 获取传递的参数
class NoteDetailPage extends StatelessWidget {
  
  Widget build(BuildContext context) {
    final note = ModalRoute.of(context)!.settings.arguments as Note;
    return Scaffold(/* ... */);
  }
}

routes属性定义路由表,键是路由名称,值是页面构建函数。pushNamed使用路由名称导航,arguments传递参数。ModalRoute.of(context).settings.arguments获取传递的参数。命名路由使路由配置集中管理,便于维护和修改。

路由生成器

onGenerateRoute提供更灵活的路由处理。

MaterialApp(
  onGenerateRoute: (settings) {
    switch (settings.name) {
      case '/':
        return MaterialPageRoute(builder: (_) => NotesListPage());
      case '/note/detail':
        final note = settings.arguments as Note;
        return MaterialPageRoute(
          builder: (_) => NoteDetailPage(note: note),
        );
      case '/note/edit':
        final noteId = settings.arguments as String?;
        return MaterialPageRoute(
          builder: (_) => NoteEditPage(noteId: noteId),
        );
      default:
        return MaterialPageRoute(builder: (_) => NotFoundPage());
    }
  },
)

onGenerateRoute在每次导航时被调用,可以根据路由名称和参数动态创建页面。这种方式可以在路由层面进行参数解析和类型转换,页面组件接收强类型的参数。default分支处理未知路由,显示404页面。

OpenHarmony页面导航

OpenHarmony使用router模块进行页面导航。

import router from '@ohos.router';

@Entry
@Component
struct NotesListPage {
  @State noteList: NoteItem[] = []
  
  openNoteDetail(note: NoteItem) {
    router.pushUrl({
      url: 'pages/NoteDetailPage',
      params: {
        noteId: note.id,
        noteTitle: note.title
      }
    })
  }
  
  build() {
    List() {
      ForEach(this.noteList, (item: NoteItem) => {
        ListItem() {
          Text(item.title)
            .onClick(() => {
              this.openNoteDetail(item)
            })
        }
      })
    }
  }
}

router.pushUrl将新页面压入导航栈,url指定目标页面的路径,params传递参数对象。页面路径对应src/main/ets/pages目录下的文件。这种基于URL的导航方式与Web开发类似,易于理解和使用。

@Entry
@Component
struct NoteDetailPage {
  @State noteId: string = ''
  @State noteTitle: string = ''
  
  aboutToAppear() {
    let params = router.getParams() as Record<string, string>
    this.noteId = params['noteId'] || ''
    this.noteTitle = params['noteTitle'] || ''
  }
  
  goBack() {
    router.back()
  }
  
  goBackWithResult() {
    router.back({
      url: 'pages/NotesListPage',
      params: {
        refresh: true
      }
    })
  }
  
  build() {
    Column() {
      Text(this.noteTitle)
        .fontSize(20)
      
      Button('返回')
        .onClick(() => {
          this.goBack()
        })
    }
  }
}

router.getParams()获取传递的参数,返回对象类型需要进行类型断言。aboutToAppear生命周期方法在页面即将显示时调用,适合初始化参数。router.back()返回上一页,可以通过params传递返回结果。这种设计让页面间的数据传递变得简单直接。

路由拦截与守卫

某些页面需要登录才能访问。

MaterialApp(
  onGenerateRoute: (settings) {
    // 需要登录的页面列表
    final protectedRoutes = ['/note/edit', '/settings'];
    
    if (protectedRoutes.contains(settings.name)) {
      if (!AuthService.isLoggedIn) {
        return MaterialPageRoute(
          builder: (_) => LoginPage(redirectTo: settings.name),
        );
      }
    }
    
    // 正常路由处理...
  },
)

在onGenerateRoute中检查用户登录状态,未登录时重定向到登录页面。redirectTo参数记录原本要访问的页面,登录成功后可以跳转回去。这种路由守卫模式可以集中处理权限控制逻辑。

class LoginPage extends StatelessWidget {
  final String? redirectTo;
  
  const LoginPage({this.redirectTo});
  
  void _onLoginSuccess(BuildContext context) {
    if (redirectTo != null) {
      Navigator.pushReplacementNamed(context, redirectTo!);
    } else {
      Navigator.pushReplacementNamed(context, '/');
    }
  }
}

登录成功后使用pushReplacementNamed替换当前页面,避免用户按返回键回到登录页。如果有redirectTo参数则跳转到原目标页面,否则跳转到首页。

深度链接处理

深度链接允许从外部直接打开应用的特定页面。

MaterialApp(
  onGenerateRoute: (settings) {
    final uri = Uri.parse(settings.name ?? '');
    
    if (uri.pathSegments.length == 2 && uri.pathSegments[0] == 'note') {
      final noteId = uri.pathSegments[1];
      return MaterialPageRoute(
        builder: (_) => NoteDetailPage(noteId: noteId),
      );
    }
    
    // 其他路由处理...
  },
)

解析URL路径来确定目标页面和参数。/note/123这样的URL会被解析为打开ID为123的笔记详情页。深度链接使应用可以响应外部链接,如分享链接、通知点击等场景。

导航状态管理

复杂应用可能需要管理导航状态。

class NavigationService {
  static final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
  
  static Future<T?> navigateTo<T>(String routeName, {Object? arguments}) {
    return navigatorKey.currentState!.pushNamed<T>(routeName, arguments: arguments);
  }
  
  static void goBack<T>([T? result]) {
    navigatorKey.currentState!.pop<T>(result);
  }
  
  static void navigateAndRemoveUntil(String routeName) {
    navigatorKey.currentState!.pushNamedAndRemoveUntil(
      routeName,
      (route) => false,
    );
  }
}

// 在MaterialApp中使用
MaterialApp(
  navigatorKey: NavigationService.navigatorKey,
  // ...
)

NavigationService封装导航操作,通过GlobalKey访问NavigatorState。这种方式可以在非Widget代码中进行导航,如服务类、状态管理类等。navigateAndRemoveUntil清空导航栈并跳转,常用于登出后返回登录页。

总结

导航与路由管理是应用架构的基础设施。Flutter和OpenHarmony都提供了完善的导航API,支持页面跳转、参数传递、返回结果等功能。开发者需要根据应用复杂度选择合适的路由方案,简单应用使用基础导航即可,复杂应用可以使用命名路由或路由生成器。良好的路由设计可以让应用结构更加清晰,用户体验更加流畅。

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐