Initial Flutter project structure、

This commit is contained in:
nmbh1122@126.com
2026-07-15 12:01:04 +08:00
commit 120525741d
138 changed files with 7888 additions and 0 deletions
+582
View File
@@ -0,0 +1,582 @@
# `main.dart`、`app.dart` 和 `const App()` 说明
## `main.dart` 和 `app.dart` 应该放什么
以常见 Flutter App 项目结构来看,`main.dart``app.dart` 的职责应该分得很清楚:
- `main.dart`:负责“启动前准备”和“启动 App”
- `app.dart`:负责“描述整个 App 长什么样、用什么主题、从哪个页面开始、怎么路由”
## `main.dart` 应该放什么
`main.dart` 是程序入口,应该尽量薄。
最基础版本:
```dart
import 'package:flutter/material.dart';
import 'app/app.dart';
void main() {
runApp(const App());
}
```
如果项目刚开始,这样就够了。
## 稍微正式一点的 `main.dart`
如果后面需要初始化一些东西,可以这样:
```dart
import 'package:flutter/material.dart';
import 'app/app.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
runApp(const App());
}
```
`WidgetsFlutterBinding.ensureInitialized()` 的作用是:在 `runApp` 之前确保 Flutter 框架初始化完成。
什么时候需要它:
- 初始化 `SharedPreferences`
- 初始化本地数据库
- 初始化 Firebase
- 加载 `.env`
- 初始化本地存储
- 设置屏幕方向
- 初始化依赖注入
例如以后可能会变成:
```dart
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await AppStorage.init();
await Firebase.initializeApp();
runApp(const App());
}
```
但现在不需要提前写这些。
## `main.dart` 不应该放什么
不建议在 `main.dart` 里放:
- 页面 UI
- `Scaffold`
- `AppBar`
- 首页布局
- 登录逻辑
- 网络请求
- 路由表
- 大量主题配置
- 业务状态管理
也就是说,不要让 `main.dart` 变成这样:
```dart
void main() {
runApp(MaterialApp(
home: Scaffold(
appBar: AppBar(...),
body: ...,
),
));
}
```
这只适合临时 Demo,不适合项目长期维护。
## `app.dart` 应该放什么
`app.dart` 放根组件,也就是整个应用壳子。
最基础版本:
```dart
import 'package:flutter/material.dart';
import '../features/home/home_page.dart';
import 'theme.dart';
class App extends StatelessWidget {
const App({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Patbond',
debugShowCheckedModeBanner: false,
theme: AppTheme.light,
home: const HomePage(),
);
}
}
```
这里负责:
- App 标题
- 是否显示右上角 debug 标识
- 全局主题
- 首页入口
- 路由配置
- 本地化配置
- 全局导航配置
## 如果暂时没有路由系统
项目刚开始,页面少,可以这样:
```dart
return MaterialApp(
title: 'Patbond',
debugShowCheckedModeBanner: false,
theme: AppTheme.light,
home: const HomePage(),
);
```
这种最简单。
适合:
- 只有首页
- 页面数量很少
- 暂时不需要登录拦截
- 暂时不需要深链接
- 暂时不需要命名路由
## 如果使用 Flutter 自带命名路由
可以这样:
```dart
return MaterialApp(
title: 'Patbond',
debugShowCheckedModeBanner: false,
theme: AppTheme.light,
initialRoute: '/',
routes: {
'/': (_) => const HomePage(),
'/login': (_) => const LoginPage(),
'/profile': (_) => const ProfilePage(),
},
);
```
适合小到中等项目。
优点:
- 简单
- 不需要额外依赖
-`Navigator.push` 字符串散落各处更统一
缺点:
- 登录拦截不够优雅
- 嵌套路由不方便
- Web URL 管理不如 `go_router`
## 如果以后使用 `go_router`
`app.dart` 会变成:
```dart
import 'package:flutter/material.dart';
import 'router.dart';
import 'theme.dart';
class App extends StatelessWidget {
const App({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp.router(
title: 'Patbond',
debugShowCheckedModeBanner: false,
theme: AppTheme.light,
routerConfig: appRouter,
);
}
}
```
然后 `router.dart` 单独管理路由。
这种适合:
- 页面多
- Web 地址栏需要正常变化
- 有登录态跳转
- 有底部 Tab
- 有深链接
- 有权限拦截
## `app.dart` 不应该放什么
不建议在 `app.dart` 里放:
- 某个页面的具体 UI
- 首页复杂布局
- 登录表单逻辑
- 网络请求实现
- 数据模型
- 业务判断细节
- 大量工具函数
`app.dart` 是应用壳,不是业务页面。
## 推荐当前项目的版本
当前项目还很新,建议先用最简单的版本。
目录:
```text
lib/
main.dart
app/
app.dart
theme.dart
features/
home/
home_page.dart
```
`main.dart`
```dart
import 'package:flutter/material.dart';
import 'app/app.dart';
void main() {
runApp(const App());
}
```
`app/app.dart`
```dart
import 'package:flutter/material.dart';
import '../features/home/home_page.dart';
import 'theme.dart';
class App extends StatelessWidget {
const App({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Patbond',
debugShowCheckedModeBanner: false,
theme: AppTheme.light,
home: const HomePage(),
);
}
}
```
`app/theme.dart`
```dart
import 'package:flutter/material.dart';
class AppTheme {
const AppTheme._();
static ThemeData get light {
return ThemeData(
colorScheme: ColorScheme.fromSeed(
seedColor: Colors.deepPurple,
),
useMaterial3: true,
);
}
}
```
`features/home/home_page.dart`
```dart
import 'package:flutter/material.dart';
class HomePage extends StatelessWidget {
const HomePage({super.key});
@override
Widget build(BuildContext context) {
return const Scaffold(
body: Center(
child: Text('Patbond'),
),
);
}
}
```
## 职责总结
`main.dart`
```text
负责启动
```
应该包含:
```text
runApp
启动前初始化
全局异常捕获入口
```
不应该包含:
```text
页面 UI
业务逻辑
路由细节
主题细节
网络请求
```
`app.dart`
```text
负责 App 壳子
```
应该包含:
```text
MaterialApp
主题配置入口
路由配置入口
首页入口
本地化配置入口
```
不应该包含:
```text
具体页面布局
业务逻辑
接口请求
复杂状态处理
```
`home_page.dart`
```text
负责首页 UI
```
应该包含:
```text
Scaffold
AppBar
body
首页交互
首页状态
```
## 关于 `const App()`
如果写:
```dart
import 'package:flutter/material.dart';
import 'app/app.dart';
void main() {
runApp(App());
}
```
IDE 提示要去掉或加上 `const`,通常是因为 `App` 构造函数是否支持 `const`
在 Flutter 里,如果一个 Widget 可以在编译期确定,推荐写成:
```dart
runApp(const App());
```
而不是:
```dart
runApp(App());
```
## 正确写法
`main.dart`
```dart
import 'package:flutter/material.dart';
import 'app/app.dart';
void main() {
runApp(const App());
}
```
`app/app.dart` 里面要这样写:
```dart
import 'package:flutter/material.dart';
class App extends StatelessWidget {
const App({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Patbond',
debugShowCheckedModeBanner: false,
home: const Scaffold(
body: Center(
child: Text('Patbond'),
),
),
);
}
}
```
关键是这一行:
```dart
const App({super.key});
```
如果 `App` 类里没有 `const` 构造函数,比如是这样:
```dart
class App extends StatelessWidget {
App({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(...);
}
}
```
`runApp(const App());` 就不能用,IDE 可能会提示去掉 `const`
## 为什么 Flutter 推荐 `const`
`const` 的好处是:
- 编译期创建对象
- 减少运行时重复创建 Widget
- Flutter rebuild 时更容易优化
- 代码语义更清楚:这个 Widget 本身没有可变状态
对于 `StatelessWidget`,只要构造参数也都是可 `const` 的,一般都应该写 `const` 构造函数。
## 看到“让我去掉 const”的常见原因
### 原因 1`App` 构造函数不是 `const`
错误:
```dart
class App extends StatelessWidget {
App({super.key});
}
```
正确:
```dart
class App extends StatelessWidget {
const App({super.key});
}
```
### 原因 2`App` 里面继承的不是 Widget,或者写法不完整
正确:
```dart
class App extends StatelessWidget {
const App({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp();
}
}
```
### 原因 3`App` 构造函数里有非 const 默认值或初始化逻辑
例如:
```dart
class App extends StatelessWidget {
App({super.key}) {
print('init');
}
}
```
这种就不能是 `const` 构造函数。
### 原因 4:`App` 构造函数接收了运行时对象
例如:
```dart
class App extends StatelessWidget {
final DateTime now;
const App({super.key, required this.now});
}
```
调用时不能这样:
```dart
runApp(const App(now: DateTime.now()));
```
因为 `DateTime.now()` 不是编译期常量。
## 结论
优先写成:
```dart
void main() {
runApp(const App());
}
```
并确保 `App` 里面是:
```dart
class App extends StatelessWidget {
const App({super.key});
}
```
如果这样之后 IDE 还提示去掉 `const`,说明 `App` 构造函数或参数里有运行时内容。