go-router结合flutter实现一个浏览器

使用 Flutter 和 GoRouter 实现浏览器式导航

是的,你可以使用 Flutter 和 GoRouter 来实现一个类似浏览器的导航体验,包括前进、后退、跳转到特定历史记录等功能。下面是一个完整的实现方案:

1. 基本架构设计

我们需要实现以下功能:

  • 维护完整的导航历史记录
  • 支持前进/后退操作
  • 显示当前 URL
  • 支持直接跳转到历史记录中的特定位置

2. 完整实现代码

2.1 主入口和路由配置

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

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

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

  @override
  State<BrowserApp> createState() => _BrowserAppState();
}

class _BrowserAppState extends State<BrowserApp> {
  late final GoRouter _router;
  final _historyManager = NavigationHistoryManager();

  @override
  void initState() {
    super.initState();
    
    _router = GoRouter(
      routes: [
        GoRoute(
          path: '/',
          builder: (context, state) => const HomePage(title: 'Home'),
        ),
        GoRoute(
          path: '/page1',
          builder: (context, state) => const ContentPage(title: 'Page 1', color: Colors.blue),
        ),
        GoRoute(
          path: '/page2',
          builder: (context, state) => const ContentPage(title: 'Page 2', color: Colors.green),
        ),
        GoRoute(
          path: '/page3',
          builder: (context, state) => const ContentPage(title: 'Page 3', color: Colors.orange),
        ),
      ],
      observers: [_historyManager], // 添加历史记录观察者
    );
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp.router(
      routerConfig: _router,
      title: 'Flutter Browser',
      debugShowCheckedModeBanner: false,
    );
  }
}

2.2 导航历史管理器

class NavigationHistoryManager extends NavigatorObserver {
  final List<String> _history = [];
  int _currentIndex = -1;

  List<String> get history => _history;
  int get currentIndex => _currentIndex;
  String get currentRoute => _history.isNotEmpty ? _history[_currentIndex] : '';

  bool get canGoBack => _currentIndex > 0;
  bool get canGoForward => _currentIndex < _history.length - 1;

  @override
  void didPush(Route<dynamic> route, Route<dynamic>? previousRoute) {
    if (route.settings.name != null) {
      // 如果是前进操作,清除后面的历史
      if (_currentIndex < _history.length - 1) {
        _history.removeRange(_currentIndex + 1, _history.length);
      }
      
      _history.add(route.settings.name!);
      _currentIndex = _history.length - 1;
    }
  }

  @override
  void didPop(Route<dynamic> route, Route<dynamic>? previousRoute) {
    if (_currentIndex > 0) {
      _currentIndex--;
    }
  }

  void goToIndex(BuildContext context, int index) {
    if (index >= 0 && index < _history.length) {
      _currentIndex = index;
      context.go(_history[index]);
    }
  }

  void goBack(BuildContext context) {
    if (canGoBack) {
      _currentIndex--;
      context.go(_history[_currentIndex]);
    }
  }

  void goForward(BuildContext context) {
    if (canGoForward) {
      _currentIndex++;
      context.go(_history[_currentIndex]);
    }
  }
}

2.3 页面组件实现

class HomePage extends StatelessWidget {
  final String title;

  const HomePage({super.key, required this.title});

  @override
  Widget build(BuildContext context) {
    final historyManager = Navigator.of(context)
        .widget
        .observers
        .whereType<NavigationHistoryManager>()
        .first;

    return Scaffold(
      appBar: BrowserAppBar(historyManager: historyManager),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            const Text('Welcome to Flutter Browser'),
            const SizedBox(height: 20),
            Wrap(
              spacing: 10,
              children: [
                ElevatedButton(
                  onPressed: () => context.go('/page1'),
                  child: const Text('Go to Page 1'),
                ),
                ElevatedButton(
                  onPressed: () => context.go('/page2'),
                  child: const Text('Go to Page 2'),
                ),
                ElevatedButton(
                  onPressed: () => context.go('/page3'),
                  child: const Text('Go to Page 3'),
                ),
              ],
            ),
          ],
        ),
      ),
    );
  }
}

class ContentPage extends StatelessWidget {
  final String title;
  final Color color;

  const ContentPage({super.key, required this.title, required this.color});

  @override
  Widget build(BuildContext context) {
    final historyManager = Navigator.of(context)
        .widget
        .observers
        .whereType<NavigationHistoryManager>()
        .first;

    return Scaffold(
      appBar: BrowserAppBar(historyManager: historyManager),
      backgroundColor: color.withOpacity(0.2),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text(title, style: TextStyle(fontSize: 24, color: color)),
            const SizedBox(height: 20),
            Wrap(
              spacing: 10,
              children: [
                ElevatedButton(
                  onPressed: () => context.go('/'),
                  child: const Text('Go Home'),
                ),
                ElevatedButton(
                  onPressed: () => context.go('/page1'),
                  child: const Text('Go to Page 1'),
                ),
                ElevatedButton(
                  onPressed: () => context.go('/page2'),
                  child: const Text('Go to Page 2'),
                ),
                ElevatedButton(
                  onPressed: () => context.go('/page3'),
                  child: const Text('Go to Page 3'),
                ),
              ],
            ),
          ],
        ),
      ),
    );
  }
}

2.4 浏览器工具栏组件

class BrowserAppBar extends StatelessWidget implements PreferredSizeWidget {
  final NavigationHistoryManager historyManager;

  const BrowserAppBar({super.key, required this.historyManager});

  @override
  Widget build(BuildContext context) {
    return AppBar(
      title: Text(historyManager.currentRoute),
      actions: [
        IconButton(
          icon: const Icon(Icons.arrow_back),
          onPressed: historyManager.canGoBack
              ? () => historyManager.goBack(context)
              : null,
        ),
        IconButton(
          icon: const Icon(Icons.arrow_forward),
          onPressed: historyManager.canGoForward
              ? () => historyManager.goForward(context)
              : null,
        ),
        PopupMenuButton<int>(
          itemBuilder: (context) => historyManager.history
              .asMap()
              .entries
              .map((entry) => PopupMenuItem(
                    value: entry.key,
                    child: Text(
                      entry.value,
                      style: TextStyle(
                        fontWeight: entry.key == historyManager.currentIndex
                            ? FontWeight.bold
                            : FontWeight.normal,
                      ),
                    ),
                  ))
              .toList(),
          onSelected: (index) => historyManager.goToIndex(context, index),
        ),
      ],
    );
  }

  @override
  Size get preferredSize => const Size.fromHeight(kToolbarHeight);
}

更多关于go-router结合flutter实现一个浏览器的实战教程也可以访问 https://www.itying.com/category-92-b0.html

回到顶部