《Flutter篇第一章》基于GetX 和 Binding、Dio 实现的 Flutter UI 架构

发布于:2025-07-26 ⋅ 阅读:(16) ⋅ 点赞:(0)
架构流程图
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地址


网站公告

今日签到

点亮在社区的每一天
去签到