《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.2get: ^4.6.5dio: ^5.0.0get_storage: ^2.1.1shared_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);}
}