在经过了长达两周的放假学习和带薪学习后, 我终于做出了人生中第一个前端应用 (虽然甚至现在我都没搞明白 Dart 有几种数据类型), 项目地址在这里 ,演示地址在这里 下面我将介绍一下本项目使用的一些依赖, 设计结构和具体的实现.

项目依赖

总体上使用 Flutter 框架, 其他框架列表如下:

  • dio: 网络请求
  • date_format: 日期格式化
  • get: Getx 状态管理
  • logger: 日志框架
  • sp_util: 对 shared_preferences 框架的封装, 用于在内存中存储一些数据
  • intl: 时间戳和日期转换

设计结构

因为设计初期的原因多加了层 common 目录, 这个可以忽略

  • pages:
    • controller: 页面内请求的逻辑
    • view: 画页面的代码
    • binding: Getx 依赖注入
    • component:
      • body: 一些组件, 被 view 中的代码使用
  • constants: 一些常量内容, 比如 token 的名字等
  • http: 封装 dio 的 http 请求
  • middleware: 中间件, 如页面跳转鉴权等
  • models: 传输模型, 请求值和返回值的数据结构
  • routes: 页面路由
  • service: 向后端发送请求的代码, 可能会涉及到一些其他逻辑, 被 controller 调用, 调用 http 的代码发送请求; 有本地数据库时也可以将本地数据库的操作逻辑写在这里 (或者也可以单开一个 repository 目录也可以)
  • config: 放一些配置文件

具体实现

重点截取一些代码

定义页面路由, 中间件 和 binding:

GetPage(
  name: AppRoutes.task,
  page: () => const TaskListView(),
  binding: TaskBinding(),
  middlewares: [
    RouteAuthMiddleware(),
  ],
)

binding 依赖注入:

class LoginBinding implements Bindings {
  static final Logger _logger = Logger();

  @override
  void dependencies() {
    _logger.i('依赖注入');
    Get.lazyPut(() => UserService());
    Get.lazyPut<LoginController>(() => LoginController());
  }
}

controller:

class LoginController extends GetxController {
    // 这里就是具体的逻辑
}

view, 使用 controller 和 body:

class LoginView extends StatelessWidget {
  const LoginView({super.key});

  @override
  Widget build(BuildContext context) {
    return GetBuilder<LoginController>(
        builder: (controller) => const Scaffold(
              body: LoginBody(),
            ));
  }
}

body:

class LoginBody extends GetView<LoginController> {
  const LoginBody({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        // 这里就是正常的写页面
        // 因为继承了GetView所以可以不用Get.find()直接使用controller
    );
  }
}

全局配置类 (注意不要使用 GetMaterialApp 中 onInit 初始化一些变量, 可能会有问题):

class GlobalConfig {
  static final Logger _logger = Logger();

  static Future init() async {
    _logger.i('开始全局初始化');
    await SpUtil.getInstance();
    Get.put<UserService>(UserService());
    _logger.i('全局初始化结束');
  }
}

启动类:

Future<void> main() async {
  await GlobalConfig.init();
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return GetMaterialApp(
      debugShowCheckedModeBanner: false,
      enableLog: true,
      initialRoute: AppPages.initial,
      getPages: AppPages.routes,
    );
  }
}

本地调试命令:

# 防止CORS报错
flutter run -d chrome --web-renderer html --web-browser-flag "--disable-web-security"

打包成 Web 应用:

flutter build web --web-renderer html