架构流程图
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地址