# Bilibili **Repository Path**: kingstk/bilibili ## Basic Information - **Project Name**: Bilibili - **Description**: 使用 Flutter 框架开发的高仿Bilibili 客户端应用,2026年最新。 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-04-18 - **Last Updated**: 2026-07-22 ## Categories & Tags **Categories**: Uncategorized **Tags**: Flutter, Dart, Kotlin ## README # Bilibili Flutter 项目 一个基于 Flutter 开发的 Bilibili 客户端项目。 ## 项目介绍 本项目是一个使用 Flutter 框架开发的 Bilibili 客户端应用,采用现代化的架构设计和最佳实践,提供流畅的用户体验。 ## 技术栈 - **框架**:Flutter - **状态管理**:Provider - **网络请求**:Dio(封装适配器模式) - **路由管理**:go_router - **本地存储**:shared_preferences ## 快速开始 ### 环境搭建 1. 确保已安装 Flutter SDK:[Flutter 安装指南](https://docs.flutter.dev/get-started/install) 2. 克隆项目到本地 3. 安装依赖 ### 项目启动 ```bash # 查看连接的设备 flutter devices # 运行项目到指定设备 flutter run -d device_id ``` ### 依赖安装 ```bash # 安装 dio 网络请求库 dart pub add dio 或 flutter pub add dio # 安装 provider 状态管理库 flutter pub add provider # 安装 shared_preferences 本地存储库 flutter pub add shared_preferences # 安装 go_router 路由管理库 flutter pub add go_router # 重新获取所有依赖 flutter pub get ``` ## 网络请求封装 ### 架构设计 本项目采用 **适配器模式** 实现网络请求封装,具有良好的扩展性和可替换性。 ``` lib/http/ ├── core/ # 核心层 │ ├── hi_net.dart # 网络请求管理器(单例) │ ├── hi_net_adapter.dart # 适配器抽象接口 │ ├── dio_adapter.dart # Dio 适配器实现 │ ├── mock_adapter.dart # Mock 适配器(测试用) │ └── hi_error.dart # 统一错误处理 └── request/ # 请求层 ├── base_request.dart # 请求抽象基类 └── test_request.dart # 具体请求实现示例 ``` ### 核心组件 #### 1. HiNet(网络请求管理器) 单例模式,统一管理网络请求入口,负责错误处理和状态码分发。 **优势**: - 统一错误处理(401 需要登录、403 需要授权) - 单例模式确保全局唯一性 - 支持适配器切换(生产环境用 Dio,测试环境用 Mock) #### 2. HiNetAdapter(适配器接口) 抽象接口,定义 `send()` 方法,任何网络库只需实现此接口即可接入。 **优势**: - 解耦网络库依赖 - 便于单元测试和集成测试 - 新增网络库只需实现接口,无需修改业务代码 #### 3. DioAdapter(Dio 实现) 使用业界主流的 Dio 库实现网络请求。 **优势**: - 支持多种 HTTP 方法(GET/POST/DELETE) - 完善的错误处理机制 - 丰富的配置选项(超时、拦截器等) #### 4. BaseRequest(请求基类) 抽象基类,定义请求的通用属性和行为,子类只需实现三个抽象方法即可。 **优势**: - 简化具体请求的实现,只需关注业务逻辑 - 链式调用 API(add()、addHeader()) - 自动处理 URL 拼接(path 参数、query 参数、http/https) - 可复用请求头、参数等通用配置 #### 5. HiError(错误处理) 统一错误类型,分为普通错误、需要登录错误、需要授权错误。 **优势**: - 明确的错误类型,便于针对性处理 - 携带错误数据,便于调试和展示 ### 使用示例 #### 1. 创建请求类 ```dart import 'base_request.dart'; // 继承 BaseRequest 抽象基类 class TestRequest extends BaseRequest { // 指定 HTTP 请求方法为 GET @override HttpMethod httpMethod() => HttpMethod.GET; // 指定该请求是否需要登录 @override bool needLogin() => false; // 指定请求路径 @override String path() => 'uapi/test/test'; } ``` #### 2. 发起请求 ```dart // 创建请求实例 var request = TestRequest(); // 添加查询参数和请求头 request.add('key', 'value').addHeader('token', 'xxx'); // 发起网络请求 HiNet.getInstance().fire(request).then((result) { // 成功处理 }).catchError((e) { if (e is NeedLogin) { // 登录失效,跳转到登录页 } else if (e is NeedAuth) { // 权限不足,提示用户 } }); ``` #### 3. 切换适配器 在 `hi_net.dart` 的 `send()` 方法中修改: ```dart // 测试环境使用 Mock(方便调试,不发送真实请求) HiNetAdapter adapter = MockAdapter(); // 生产环境使用 Dio(发送真实网络请求) HiNetAdapter adapter = DioAdapter(); ``` ### 优势总结 | 特性 | 说明 | |------|------| | **低耦合** | 适配器模式解耦网络库与业务代码 | | **易扩展** | 新增网络库只需实现 HiNetAdapter | | **易测试** | 可轻松切换 MockAdapter 进行测试 | | **易维护** | BaseRequest 统一管理请求通用逻辑 | | **类型安全** | 明确的错误类型和泛型支持 | ### 相关教程 - [Dio 官方文档](https://pub.dev/packages/dio) - [Flutter 网络请求指南](https://docs.flutter.dev/cookbook/networking) - [适配器模式详解](https://refactoringguru.cn/design-patterns/adapter) ## 路由管理(go_router) 本项目采用 **go_router** 实现声明式路由管理,支持嵌套路由、底部导航栏和深层链接。 ### 架构设计 ``` lib/ ├── router/ │ └── router.dart # 路由配置(路由表、底部导航栏) └── pages/ ├── login.dart # 登录页面 ├── home.dart # 首页 ├── follow.dart # 关注页面 ├── vip.dart # 会员购页面 ├── mine.dart # 我的页面 └── settings.dart # 设置页面(无导航栏) ``` ### 核心组件 #### 1. GoRouter(路由管理器) 声明式路由配置,支持嵌套路由和 `StatefulShellRoute` 实现底部导航栏。 **优势**: - 声明式路由配置,代码清晰易维护 - 支持深层链接和 URL 同步 - 内置路由守卫和重定向 - 与 Flutter 状态管理无缝集成 #### 2. StatefulShellRoute(带状态的 Shell 路由) 用于实现底部导航栏,支持多分支嵌套路由,保持各分支的导航状态。 **优势**: - 各底部导航项独立管理导航状态 - 切换标签时保留各页面的滚动位置 - 支持动画过渡效果 - 与 `StatefulNavigationShell` 配合实现复杂导航 #### 3. StatefulNavigationShell(导航 Shell) 封装底部导航栏的组件,管理各分支的切换和导航状态。 **优势**: - 统一管理底部导航栏逻辑 - 支持 `goBranch()` 方法切换分支 - 自动同步底部导航栏选中状态 ### 路由配置示例 #### 1. 基本路由配置 ```dart // 导入 go_router 和页面文件 import 'package:go_router/go_router.dart'; import 'package:first/pages/home.dart'; import 'package:first/pages/settings.dart'; // 创建路由配置实例 final GoRouter router = GoRouter( // 设置初始路由为首页 initialLocation: '/', routes: [ // 首页路由 GoRoute( path: '/', // builder 回调返回对应的页面组件 builder: (context, state) => const HomePage(), ), // 设置页面路由 GoRoute( path: '/settings', builder: (context, state) => const SettingsPage(), ), ], ); ``` #### 2. 带底部导航栏的路由配置 ```dart // 导入 go_router 和页面文件 import 'package:go_router/go_router.dart'; import 'package:first/pages/home.dart'; import 'package:first/pages/mine.dart'; import 'package:first/pages/settings.dart'; // 创建路由配置实例 final GoRouter router = GoRouter( initialLocation: '/', routes: [ // 使用 StatefulShellRoute 实现带底部导航栏的嵌套路由 StatefulShellRoute.indexedStack( // builder 返回包含底部导航栏的页面 builder: (context, state, navigationShell) { return MainPage(navigationShell: navigationShell); }, // 定义多个分支,每个分支对应一个底部导航项 branches: [ // 第一个分支:首页 StatefulShellBranch( routes: [ GoRoute( path: '/', builder: (context, state) => const HomePage(), ), ], ), // 第二个分支:我的 StatefulShellBranch( routes: [ GoRoute( path: '/mine', builder: (context, state) => const MinePage(), ), ], ), ], ), // 设置页面(不在底部导航栏中,独立路由) GoRoute( path: '/settings', builder: (context, state) => const SettingsPage(), ), ], ); ``` ### 底部导航栏实现 #### MainPage 组件实现 ```dart // 主页面组件,包含底部导航栏 class MainPage extends StatefulWidget { const MainPage({super.key, required this.navigationShell}); // 导航 Shell,用于管理分支切换 final StatefulNavigationShell navigationShell; @override State createState() => _MainPageState(); } class _MainPageState extends State { // 当前选中的导航索引 int _selectedIndex = 0; // 底部导航栏的导航项配置 final List _items = [ // 首页导航项 const BottomNavigationBarItem(icon: Icon(Icons.home), label: '首页'), // 关注导航项 const BottomNavigationBarItem(icon: Icon(Icons.favorite), label: '关注'), // +号导航项(中间位置,用于发布内容) const BottomNavigationBarItem(icon: Icon(Icons.add_circle_outline), label: ''), // 会员购导航项 const BottomNavigationBarItem(icon: Icon(Icons.shopping_bag), label: '会员购'), // 我的导航项 const BottomNavigationBarItem(icon: Icon(Icons.person), label: '我的'), ]; // 导航项点击处理 void _onItemTapped(int index) { // +号按钮特殊处理,不进行页面切换 if (index == 2) { print('点击了 + 号按钮'); return; } // 计算分支索引(因为 + 号位置没有对应分支,需要偏移) int branchIndex = index > 2 ? index - 1 : index; setState(() { _selectedIndex = index; }); // 切换到对应分支 // initialLocation: true 表示如果点击的是当前分支,则重置到该分支的初始页面 widget.navigationShell.goBranch( branchIndex, initialLocation: branchIndex == widget.navigationShell.currentIndex, ); } @override Widget build(BuildContext context) { return Scaffold( // 显示当前分支的页面 body: widget.navigationShell, // 底部导航栏 bottomNavigationBar: BottomNavigationBar( items: _items, currentIndex: _selectedIndex, onTap: _onItemTapped, type: BottomNavigationBarType.fixed, ), ); } } ``` ### 页面导航 #### 1. 跳转页面 ```dart // 使用 push 跳转到普通页面,会添加新页面到路由栈(可返回) context.push('/settings'); // 使用 go 跳转到页面并替换当前页面(不可返回) context.go('/home'); // 使用 go 跳转到登录页并清除路由栈(退出登录常用) context.go('/login'); ``` #### 2. 返回上一页 ```dart // 从路由栈中弹出当前页面,返回上一个页面 context.pop(); ``` #### 3. 带参数跳转 ```dart // 定义带参数的路由,使用 :id 作为路径参数占位符 GoRoute( path: '/user/:id', builder: (context, state) { // 从路径参数中获取 id final userId = state.pathParameters['id']; return UserPage(userId: userId!); }, ); // 跳转到用户详情页,传递 id 参数 context.push('/user/123'); ``` ### 在 main.dart 中使用 ```dart // 导入 Flutter 核心库 import 'package:flutter/material.dart'; // 导入 Provider 状态管理 import 'package:provider/provider.dart'; // 导入主题 Provider import 'package:first/theme/theme_provider.dart'; // 导入路由配置 import 'package:first/router/router.dart'; // 应用入口函数 void main() { runApp( // 使用 Provider 进行状态管理 ChangeNotifierProvider( create: (context) => ThemeProvider(), child: const MyApp(), ), ); } // 应用根组件 class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp.router( // 应用标题 title: 'Flutter Demo', // 隐藏 debug 模式横幅 debugShowCheckedModeBanner: false, // 使用 Provider 中的主题数据 theme: Provider.of(context).themeData, // 配置路由 routerConfig: router, ); } } ``` ### 优势总结 | 特性 | 说明 | |------|------| | **声明式路由** | 配置清晰,易于维护 | | **深层链接** | 支持 URL 同步和直接访问 | | **嵌套路由** | 支持多层级导航结构 | | **状态保持** | 底部导航切换时保持页面状态 | | **类型安全** | 编译时检查路由配置 | | **动画过渡** | 内置路由切换动画 | ### 相关教程 - [go_router 官方文档](https://pub.dev/packages/go_router) - [Flutter 导航指南](https://docs.flutter.dev/cookbook/navigation) - [StatefulShellRoute 教程](https://docs.flutter.dev/ui/navigation/router)