《Flutter篇第一章》基于GetX 和 Binding、Dio 实现的 Flutter UI 架构
架构流程图
lib/├── app/│ ├── bindings/ # 依赖注入绑定│ │ ├── auth_binding.dart│ │ └── main_binding.dart│ ├── modules/ # 功能模块│ │ ├── auth/ # 登录模块│ │ │ ├── auth_controller.dart│ │ │ └── auth_page.dart│ │ └── main/ # 主模块│ │ ├── tabs/ # 四个Tab页│ │ │ ├── home/│ │ │ ├── explore/│ │ │ ├── cart/│ │ │ └── profile/│ │ ├── main_controller.dart│ │ └── main_page.dart│ └── routes/ # 路由管理│ ├── app_pages.dart│ └── app_routes.dart├── data/ # 数据层│ ├── local/ # 本地存储│ │ └── storage_service.dart│ ├── network/ # 网络请求│ │ ├── api_service.dart│ │ ├── base_provider.dart│ │ └── interceptors.dart│ └── repositories/ # 仓库│ └── auth_repository.dart├── core/ # 核心工具│ ├── constants/ # 常量│ │ └── strings.dart│ └── utils/ # 工具类│ └── extensions.dart└── splash_page.dart # 启动页
架构特点
框架特点 分层架构: 视图层: 负责UI展示 控制器层: 处理业务逻辑 仓库层: 聚合数据源 服务层: 提供基础能力(网络、存储) 状态管理: 使用GetX的响应式编程(.obs + Obx()) 自动内存管理,无需手动dispose 细粒度状态更新 依赖管理: GetX Binding自动管理依赖生命周期 全局服务使用permanent: true保持常驻 页面级控制器按需创建 路由管理: 命名路由系统 中间件支持(如登录验证) 平滑的页面过渡动画 网络模块: Dio封装+拦截器 Token自动管理 统一错误处理 本地存储: GetStorage轻量级键值存储 支持对象存储 自动序列化/反序列化
实现
1、pubspec.yaml 中配置需要的组件库
cupertino_icons: ^1.0.2 get: ^4.6.5 dio: ^5.0.0 get_storage: ^2.1.1 shared_preferences: ^2.2.1
2、主入口 - lib/main.dart
import \'package:flutter/material.dart\';import \'package:flutter/services.dart\';import \'package:get/get.dart\';import \'package:get_storage/get_storage.dart\';import \'app/bindings/initial_binding.dart\';import \'app/routes/app_pages.dart\';import \'app/routes/app_routes.dart\';void main() async { WidgetsFlutterBinding.ensureInitialized(); await GetStorage.init(); SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle( systemNavigationBarColor: Colors.transparent, )); runApp(const MyApp());}class MyApp extends StatelessWidget { const MyApp({super.key}); Widget build(BuildContext context) { return GetMaterialApp( title: \'Flutter GetX Template\', initialBinding: InitialBinding(), initialRoute: Routes.SPLASH, getPages: AppPages.routes, theme: ThemeData(primarySwatch: Colors.blue), debugShowCheckedModeBanner: false, ); }}
3、lib/splash_page.dart 注释部分可以加入登录判断,也就是token 或者是其他auth
import \'package:flutter/material.dart\';import \'package:get/get.dart\';import \'app/routes/app_routes.dart\';import \'data/local/storage_service.dart\';class SplashPage extends StatelessWidget { const SplashPage({super.key}); Widget build(BuildContext context) { Future.delayed(const Duration(seconds: 2), () { final storage = Get.find<StorageService>(); // if (storage.token != null) { // Get.offNamed(Routes.MAIN); // } else { // Get.offNamed(Routes.LOGIN); // } Get.offNamed(Routes.MAIN); }); return const Scaffold( body: Center( child: FlutterLogo(size: 100), ), ); }}
4、路由管理 - lib/app/routes/app_routes.dart
abstract class Routes { static const SPLASH = \'/\'; static const LOGIN = \'/login\'; static const MAIN = \'/main\';}
5、 路由配置 - lib/app/routes/app_pages.dart
import \'package:get/route_manager.dart\';import \'../../splash_page.dart\';import \'../bindings/auth_binding.dart\';import \'../bindings/main_binding.dart\';import \'../modules/auth/auth_page.dart\';import \'../modules/main/main_page.dart\';import \'app_routes.dart\';class AppPages { static final routes = [ GetPage(name: Routes.SPLASH, page: () => const SplashPage()), GetPage( name: Routes.LOGIN, page: () => AuthPage(), binding: AuthBinding(), ), GetPage( name: Routes.MAIN, page: () => MainPage(), binding: MainBinding(), transition: Transition.fadeIn, ), ];}
6、网络请求 - lib/app/data/network/api_service.dart
import \'package:dio/dio.dart\';import \'package:get/get.dart\' hide Response;import \'../../app/routes/app_routes.dart\';import \'../local/storage_service.dart\';class ApiService extends GetxService { late Dio _dio; ApiService() { _dio = Dio(BaseOptions( baseUrl: \'https://api.example.com\', connectTimeout: 5000.seconds, receiveTimeout: 3000.seconds, )); _dio.interceptors.add(InterceptorsWrapper( onRequest: (options, handler) { final token = Get.find<StorageService>().token; if (token != null) { options.headers[\'Authorization\'] = \'Bearer $token\'; } return handler.next(options); }, onError: (error, handler) { if (error.response?.statusCode == 401) { Get.offAllNamed(Routes.LOGIN); } return handler.next(error); }, )); } Future<Response> get(String path, {Map<String, dynamic>? params}) { return _dio.get(path, queryParameters: params); } Future<Response> post(String path, {dynamic data}) { return _dio.post(path, data: data); }}
7、本地存储 - lib/app/data/local/storage_service.dart
class StorageService extends GetxService { final _storage = GetStorage(); Future<void> saveToken(String token) => _storage.write(\'auth_token\', token); String? get token => _storage.read(\'auth_token\'); Future<void> clear() => _storage.erase();}
8、仓库层 - lib/app/data/repositories/auth_repository.dart
import \'package:get/get.dart\';import \'../network/api_service.dart\';class AuthRepository { final ApiService _apiService = Get.find(); Future<Map<String, dynamic>> login(String email, String password) async { final response = await _apiService.post(\'/auth/login\', data: { \'email\': email, \'password\': password }); return response.data; }}
9、登录模块绑定 - lib/app/bindings/auth_binding.dart
import \'package:get/get.dart\';import \'../../data/local/storage_service.dart\';import \'../../data/repositories/auth_repository.dart\';import \'../modules/auth/auth_controller.dart\';class AuthBinding implements Bindings { void dependencies() { Get.lazyPut(() => AuthController( Get.find<AuthRepository>(), Get.find<StorageService>(), )); }}
10、 登录控制器 - lib/app/modules/auth/auth_controller.dart
import \'package:get/get.dart\';import \'../../../data/local/storage_service.dart\';import \'../../../data/repositories/auth_repository.dart\';import \'../../routes/app_routes.dart\';class AuthController extends GetxController { final AuthRepository _repo; final StorageService _storage; AuthController(this._repo, this._storage); final isLoading = false.obs; Future<void> login(String email, String password) async { try { isLoading(true); final response = await _repo.login(email, password); await _storage.saveToken(response[\'token\']); Get.offAllNamed(Routes.MAIN); } catch (e) { Get.snackbar(\'Error\', \'Login failed\'); } finally { isLoading(false); } }}
11、 登录页面 - lib/app/modules/auth/auth_page.dart
import \'package:flutter/material.dart\';import \'package:get/get.dart\';import \'auth_controller.dart\';class AuthPage extends GetView<AuthController> { final _emailController = TextEditingController(); final _passwordController = TextEditingController(); AuthPage({super.key}); Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text(\'Login\')), body: Padding( padding: const EdgeInsets.all(20.0), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ TextField( controller: _emailController, decoration: const InputDecoration(labelText: \'Email\'), ), const SizedBox(height: 16), TextField( controller: _passwordController, decoration: const InputDecoration(labelText: \'Password\'), obscureText: true, ), const SizedBox(height: 24), Obx(() => controller.isLoading.value ? const CircularProgressIndicator() : ElevatedButton( onPressed: () => controller.login( _emailController.text, _passwordController.text, ), child: const Text(\'Login\'), )), ], ), ), ); }}
12、 主模块绑定 - lib/app/bindings/main_binding.dart
import \'package:get/get.dart\';import \'../modules/main/main_controller.dart\';import \'../modules/main/tabs/cart/cart_controller.dart\';import \'../modules/main/tabs/explore/explore_controller.dart\';import \'../modules/main/tabs/home/home_controller.dart\';import \'../modules/main/tabs/profile/profile_controller.dart\';class MainBinding implements Bindings { void dependencies() { Get.lazyPut(() => MainController()); Get.lazyPut(() => HomeController()); Get.lazyPut(() => ExploreController()); Get.lazyPut(() => CartController()); Get.lazyPut(() => ProfileController()); }}
13、 主控制器 - lib/app/modules/main/main_controller.dart
import \'package:get/get.dart\';class MainController extends GetxController { final RxInt currentIndex = 0.obs; void changeTab(int index) => currentIndex.value = index;}
14、 主页面 - lib/app/modules/main/main_page.dart
import \'package:flutter/material.dart\';import \'package:get/get.dart\';import \'main_controller.dart\';import \'tabs/cart/cart_page.dart\';import \'tabs/explore/explore_page.dart\';import \'tabs/home/home_page.dart\';import \'tabs/profile/profile_page.dart\';class MainPage extends GetView<MainController> { final List<Widget> pages = [ const HomePage(), const ExplorePage(), const CartPage(), const ProfilePage(), ]; MainPage({super.key}); Widget build(BuildContext context) { return Scaffold( body: Obx(() => pages[controller.currentIndex.value]), bottomNavigationBar: Obx( () => BottomNavigationBar( type: BottomNavigationBarType.fixed, // 设置为fixed类型,否则背景色不生效 currentIndex: controller.currentIndex.value, onTap: controller.changeTab, backgroundColor: Colors.white, selectedItemColor: Colors.amber, unselectedItemColor: Colors.black, items: const [ BottomNavigationBarItem(icon: Icon(Icons.home), label: \'Home\'), BottomNavigationBarItem(icon: Icon(Icons.explore), label: \'Explore\'), BottomNavigationBarItem(icon: Icon(Icons.shopping_cart), label: \'Cart\'), BottomNavigationBarItem(icon: Icon(Icons.person), label: \'Profile\'), ], ), ), ); }}
15、 Tab页实现 (以Home页为例) - lib/app/modules/main/tabs/home/home_controller.dart
import \'package:get/get.dart\';import \'package:get_storage/get_storage.dart\';class HomeController extends GetxController { var welcomeMessage = \'\'.obs; void onInit() { super.onInit(); // 模拟数据加载 Future.delayed(const Duration(seconds: 1), () { welcomeMessage.value = \'Welcome back, John!\'; }); }}
17、 Tab页面 - lib/app/modules/main/tabs/home/home_page.dart
import \'package:flutter/material.dart\';import \'package:get/get.dart\';import \'home_controller.dart\';class HomePage extends GetView<HomeController> { const HomePage({super.key}); Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text(\'Home\')), body: Center( child: Obx(() => Text( controller.welcomeMessage.value, style: const TextStyle(fontSize: 24), )), ), ); }}
17、其他Tab页 (类似实现) 新page都必须按照以下格式写
import \'package:flutter/material.dart\';import \'package:get/get.dart\';import \'cart_controller.dart\';class CartPage extends GetView<CartController> { const CartPage({super.key}); Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text(\'Cart\')), body: const Center(child: Text(\'Cart Content\')), ); }}import \'package:get/get.dart\';class CartController extends GetxController { final welcomeMessage = \'Hello, User!\'.obs; void onInit() { super.onInit(); }}
18、 初始化绑定 - lib/app/bindings/initial_binding.dart
import \'package:get/get.dart\';import \'../../data/local/storage_service.dart\';import \'../../data/network/api_service.dart\';import \'../../data/repositories/auth_repository.dart\';class InitialBinding implements Bindings { void dependencies() { // 持久化服务 Get.put(StorageService(), permanent: true); Get.put(ApiService(), permanent: true); // 仓库 Get.lazyPut(() => AuthRepository(), fenix: true); }}
代码已上传Github
github地址