架构模式-①概览

[Toc]

前言:架构模式、框架、设计模式的概念

iOS—架构模式、框架、设计模式的理解

架构模式:架构模式的出现是为了管理复杂的应用程序,这样可以在一个时间内专门关注一个方面。例如,您可以在不依赖业务逻辑的情况下专注于视图设计。同时也让应用程序的测试更加容易。同时也简化了分组开发。不同的开发人员可同时开发视图、控制器逻辑和业务逻辑。我们经常说的MVC架构MVVM架构属于此类。

框架:这个最好理解了,通常是代码重用。框架与设计模式的概念容易弄混,两者有相似之处,但却有着根本的不同。设计模式是对在某种环境中反复出现的问题以及解决该问题的方案的描述规范,它比框架更抽象;框架为已经解决问题的具体实现方法,能直接执行或复用;设计模式是比框架更小的元素,一个框架中往往含有一种或多种设计模式。Xcode自带的FoundationUIKit,以及我们经常使用的AFNetworkingMJExtensionSVProgressHUD就属于这一类。

设计模式:设计模式可以通俗的理解为实现/解决某些问题,而形成的解决方案规范。增加代码的可重用性,让代码能更容易理解和可靠。我们通常说所的代理模式迭代器模式策略模式就属于这一类。对各种设计模式的了解可以帮助我们更快的解决编程过程中遇到的问题。

三者关系:架构(动词)>框架>设计模式。

软件通过架构,可以设计出很多不同的框架。在一个框架中,也可以使用很多的设计模式。设计模式不是哪儿哪儿都可以用的,只有当出现了某一特定的问题时,才利用设计模式去解决。设计模式不是用的越多越好,在维护的时候,过多的设计模式会极大的增添维护成本。

一、架构模式

1、架构模式的演变

1.1、MVC

1、架构的设计其实是为了更好的维护和迭代,而不是只考虑眼前当下的开发。

2、最基本的架构是MVC,试想下如果我一个应用的所有功能都是像”关于“功能的,那我还需要其他架构干嘛?显然MVC就已经很足够,而且还显得不冗余(这里指的是设计或者粒度不冗余)。

3、其他的架构都是从MVC演变而来。再讲其他架构的演变之前,我们先明确最原始的MVC代码是如何的。

①、首先M主要负责数据、V主要负责视图、C主要负责数据的视图显示。而这里的M是原始的瘦Model

所以,我们拿常见的”我的”页面来举例,部分原始的MVC代码一般为如下样子:

1
2
3
4
5
6
7
8
9
UserModel


View


ViewController
self.userInfoView.name = user.name;
self.userInfoView.sexString = user.sex == 1? "女" :"男";

4、在MVC+瘦Model的演变下,还有MVC+胖Model

胖Model就是瘦Model+部分弱业务逻辑(这些弱业务重复一般都是经常出现,或者说是要求可复用性的,如根据枚举获取性别字符串)。它使得Controller可以从胖Model这里拿到数据之后,不用额外做操作或者只要做非常少的操作,就能够将数据直接应用在View上.

1
2
3
4
5
6
7
8
9
UserModel
+ (NSString *)sexStringFrom:(int)sex;

View


ViewController
self.userInfoView.name = user.name;
self.userInfoView.sexString = [UserModel sexStringFrom:user.sex];

1.2、MVP?

5、有时候在MVC+瘦Model的演变下,不是转成MVC+胖Model,而是MVC+瘦Model+Helper

其实这个思路已经很接近MVP了、但是还差提点。MVP还需要为View提供数据

1
2
3
4
5
6
7
8
9
10
UserModel

View

UserHelper
+ (NSString *)sexStringFrom:(int)sex;

ViewController
self.userInfoView.name = user.name;
self.userInfoView.sexString = [UserHelper sexStringFrom:user.sex];

1.3、MVVM

6、关于MVC+瘦Model+Helper,还可以改为MVC+瘦Model+ViewModel

刚才的胖Model只从Controller移植走了一些简单的弱业务。

而ViewModel则干脆把数据的处理全部从Controller移植了出去。

1
2
3
4
5
6
7
8
9
10
11
12
13
UserModel

View

UserViewModel
name
sexString
- (id)initWithName:(NSString *)name sex:(int)sex;


ViewController
self.userInfoView.name = userViewModel.name;
self.userInfoView.sexString = userViewModel.sexString;

1.4、MVCS -> MVVM+S

拿有数据操作的”购物车“页面来说,

假设其除了有网络数据请求外,还有本地数据库处理。(附:购物车是否需要本地数据库,个人认为是基于服务端的压力来考虑,不然一般是放在服务端合适点。压力设想如下,千万级用户,购物车加减商品频繁,假设放在后台,则操作和计算也就频繁,压力也就大了。如果放在前端,则后台只需要负责是否可加减商品的检验,而不用再建购物车数据表和计算价格等,压力就小了。)

每个页面一般都会有网络请求,这些网络请求最原始的时候,是放在ViewController,也就是MVC的C中。

为了避免MVC中的ViewController后期变得十分臃肿。我们肯定要在MVC的基础上额外增加一层来处理这个。

综合考虑后,我们选用MVCS来处理。

其中S代表着Store,且我们这里的S代表着是整个模块的S,而不是每个页面都有一个S。其下层又包含着Storage和Service,即 数据库DB部分网络Network部分

Store类名,个人习惯是使用Manager来命令。如UserManager。

MVCS1

所以,最终我们选用的是 MVCS+MVVM 或者说 MVVM+S。

S层(service 层)提供一种和外界(比如远程服务 API 或文件系统)交互的独立机制。

MVVM+S 纵向的数据流就不用多说,横向的数据流,我们采用自定义的类系统通知。区别在于它有着系统Notification的一对多方便,又有着delegate的接口对接方便。

image-20200917024611311

抛开数据共享问题(如每个请求都要带userToken),每个页面

二、架构分层(Architectural Layering)

架构分层是一种将系统分解为多个逻辑层次的方法,每一层都有特定的职责和功能。

  • 架构分层有助于实现关注点分离(Separation of Concerns),使得系统的不同部分可以独立开发和维护。
  • 典型的分层包括表示层(或用户界面层)、业务逻辑层、数据访问层等。

架构分层图片来源于 《架构分层.graffle》中的【一、架构分层】

其他图片见我的项目 CJStandardProject 中的 Screenshots

其他参考文章

iOS:

三、常见的项目目录结构

常见的项目目录结构:

框架①项目目录结构

项目目录结构规范说明 请点击本链接跳转下文附录进行查看

完整的 项目目录结构树 请点击链接,跳转到本问附文中查看。

四、页面与服务类设计

1、核心理念

页面执行自己的动作,调用Service服务类。无需关心数据请求、数据请求结束后是否需要缓存等等。

Service服务中心,内部包含请求和可能的数据存储等处理,不对外暴露。

2、设计举例

页面调用服务类举例

2.1、设计

arc_layer_2

图片来源于 《架构分层.graffle》中的【二、页面与服务类设计】

2.2、服务类中的代码演示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
// UserRequest
class UserService {
// 登录-普通
static login_normal(String mobile, String smsCode) {
return UserRequest.login_normal(
mobile: mobile,
smsCode: smsCode,
completeBlock: (UserModel userModel) {
UserInfoManager().loginSuccessWithUserModel(userModel);
},
);
}
}

class UserRequest {
// 登录-普通
static login_normal({
String mobile,
String smsCode,
void Function(UserModel userModel) completeBlock,
}) {
return AppNetworkKit.post(
UrlPath.doLogin,
params: {
"mobile": mobile,
"smsCode": smsCode,
"grantType": "sms_code",
},
).then((ResponseModel responseModel) {
if (responseModel.isSuccess) {
User_manager_bean baseUserModel;
if (responseModel.result != null) {
baseUserModel = UserModel.fromJson(responseModel.result);
} else {
baseUserModel = null;
}
completeBlock(baseUserModel);
} else {
completeBlock(null);
}
});
}
}

附一:项目目录结构规范说明

一个完整的项目目录结构一定至少包含有以下几个方面。

附:完整的 项目目录结构树 请点击链接,跳转到本问附文中查看。

1、程序入口

1
│  ├─ main.dart

2、依赖库package

1
2
3
4
5
├─ package
│ ├─ network
│ └─ route
│ ├─ errors
│ └─ manager

项目依赖库,统一存放在package下。

3、资源文件assets

1
2
3
4
5
6
7
8
9
├─ assets
│ ├─ fonts
│ ├─ images
│ │ ├─ base
│ │ ├─ order
│ │ └─ user
│ └─ json
│ ├─ config
│ └─ mock

资源文件:统一存放在assets下。区分字体fonts、图片images、json(eg城市配置信息)

4、公共组件common

1
2
3
4
5
6
7
8
9
10
├─ lib
│ ├─ common
│ │ ├─ extensions
│ │ │ └─ num_extension.dart
│ │ ├─ styles
│ │ │ └─ colors.dart
│ │ │ └─ typography.dart
│ │ └─ widgets
│ │ ├─ common_button.dart
│ │ └─ common_text.dart

common/:包含通用的小部件、样式和颜色等。

——-widgets:包含通用的小部件,例如按钮、文本、图标等。

——-styles: 包含通用的颜色和字体等。例如,colors.dart 文件中可以定义一些通用的颜色常量,typography.dart 文件中可以定义一些通用的字体样式(RegularTextStyle、BoldTextStyle、MediumTextStyle、DDINPROBoldTextStyle)。

——-extensions:系统类的扩展方法(eg: 20.w_pt_bj)

5、通用工具util

1
2
│  └─ util
│ └─ textsize_util.dart

utils/:包含一些通用的工具函数或类,例如日期格式化、字符串处理、数学计算等。

6、数据模型models

1
2
3
4
5
6
7
8
9
10
11
12
│  ├─ model
│ │ ├─ global_config
│ │ │ ├─ app_config_model.dart
│ │ │ ├─ game_config_model.dart
│ │ │ ├─ global_config_model.dart
│ │ │ └─ web_config_model.dart
│ │ ├─ order
│ │ │ ├─ order_base_model.dart
│ │ │ └─ order_detail_model.dart
│ │ └─ user
│ │ ├─ user_base_model.dart
│ │ └─ user_detail_model.dart

models/:包含应用程序的数据模型,例如用户、订单等。

7、服务类service

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
│  ├─ service
│ │ ├─ global_config
│ │ │ ├─ manager
│ │ │ │ └─ global_config_service.dart
│ │ │ ├─ request
│ │ │ │ └─ global_config_request.dart
│ │ │ └─ cache
│ │ │ └─ global_config_cache.dart
│ │ └─ user
│ │ ├─ manager
│ │ │ └─ user_service.dart
│ │ ├─ request
│ │ │ └─ user_info_request.dart
│ │ └─ cache
│ │ └─ user_info_cache.dart

services/:包含应用程序的服务类或者管理中心,例如用户信息中心、配置信息中心等。

——-manager: 管理中心

——-request: 管理中心会需要的请求功能

——-cache: 管理中心可能需要的存储功能

8、模块功能module

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
│  ├─ module
│ │ ├─ auth
│ │ │ ├─ pages
│ │ │ │ ├─ login_page
│ │ │ │ │ └─ login_page.dart
│ │ │ │ └─ register_page
│ │ │ │ └─ register_page.dart
│ │ │ ├─ routes
│ │ │ │ ├─ auth_route_handel.dart
│ │ │ │ ├─ auth_route_interceptor.dart
│ │ │ │ └─ auth_route_name.dart
│ │ │ └─ widgets
│ │ │ ├─ code_input_widget.dart
│ │ │ ├─ name_input_widget.dart
│ │ │ └─ tel_input_widget.dart

modules/:包含应用程序的不同模块,例如身份验证、主页和个人资料等。

——-page: 页面

——-routes: 路由

——-widgets: 视图部件

附1、项目目录结构树

项目目录结构(以Flutter为例).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
std_demo
├─ .gitignore
├─ .metadata
├─ README.md
├─ analysis_options.yaml
├─ android
│ ├─ .gitignore
│ ├─ .gradle
│ │ ├─ 7.5
│ │ │ ├─ checksums
│ │ │ │ └─ checksums.lock
│ │ │ ├─ dependencies-accessors
│ │ │ │ ├─ dependencies-accessors.lock
│ │ │ │ └─ gc.properties
│ │ │ ├─ executionHistory
│ │ │ │ └─ executionHistory.lock
│ │ │ ├─ fileChanges
│ │ │ │ └─ last-build.bin
│ │ │ ├─ fileHashes
│ │ │ │ └─ fileHashes.lock
│ │ │ ├─ gc.properties
│ │ │ └─ vcsMetadata
│ │ ├─ buildOutputCleanup
│ │ │ ├─ buildOutputCleanup.lock
│ │ │ └─ cache.properties
│ │ └─ vcs-1
│ │ └─ gc.properties
│ ├─ app
│ │ ├─ build.gradle
│ │ └─ src
│ │ ├─ debug
│ │ │ └─ AndroidManifest.xml
│ │ ├─ main
│ │ │ ├─ AndroidManifest.xml
│ │ │ ├─ java
│ │ │ │ └─ io
│ │ │ │ └─ flutter
│ │ │ │ └─ plugins
│ │ │ │ └─ GeneratedPluginRegistrant.java
│ │ │ ├─ kotlin
│ │ │ │ └─ com
│ │ │ │ └─ example
│ │ │ │ └─ std_demo1
│ │ │ │ └─ MainActivity.kt
│ │ │ └─ res
│ │ │ ├─ drawable
│ │ │ │ └─ launch_background.xml
│ │ │ ├─ drawable-v21
│ │ │ │ └─ launch_background.xml
│ │ │ ├─ mipmap-hdpi
│ │ │ │ └─ ic_launcher.png
│ │ │ ├─ mipmap-mdpi
│ │ │ │ └─ ic_launcher.png
│ │ │ ├─ mipmap-xhdpi
│ │ │ │ └─ ic_launcher.png
│ │ │ ├─ mipmap-xxhdpi
│ │ │ │ └─ ic_launcher.png
│ │ │ ├─ mipmap-xxxhdpi
│ │ │ │ └─ ic_launcher.png
│ │ │ ├─ values
│ │ │ │ └─ styles.xml
│ │ │ └─ values-night
│ │ │ └─ styles.xml
│ │ └─ profile
│ │ └─ AndroidManifest.xml
│ ├─ build.gradle
│ ├─ gradle
│ │ └─ wrapper
│ │ ├─ gradle-wrapper.jar
│ │ └─ gradle-wrapper.properties
│ ├─ gradle.properties
│ ├─ gradlew
│ ├─ gradlew.bat
│ ├─ local.properties
│ └─ settings.gradle
├─ assets
│ ├─ fonts
│ ├─ images
│ │ ├─ base
│ │ ├─ order
│ │ └─ user
│ └─ json
│ ├─ config
│ └─ mock
├─ ios
│ ├─ .gitignore
│ ├─ Flutter
│ │ ├─ AppFrameworkInfo.plist
│ │ ├─ Debug.xcconfig
│ │ ├─ Generated.xcconfig
│ │ ├─ Release.xcconfig
│ │ └─ flutter_export_environment.sh
│ ├─ Runner
│ │ ├─ AppDelegate.swift
│ │ ├─ Assets.xcassets
│ │ │ ├─ AppIcon.appiconset
│ │ │ │ ├─ Contents.json
│ │ │ │ ├─ Icon-App-1024x1024@1x.png
│ │ │ │ ├─ Icon-App-20x20@1x.png
│ │ │ │ ├─ Icon-App-20x20@2x.png
│ │ │ │ ├─ Icon-App-20x20@3x.png
│ │ │ │ ├─ Icon-App-29x29@1x.png
│ │ │ │ ├─ Icon-App-29x29@2x.png
│ │ │ │ ├─ Icon-App-29x29@3x.png
│ │ │ │ ├─ Icon-App-40x40@1x.png
│ │ │ │ ├─ Icon-App-40x40@2x.png
│ │ │ │ ├─ Icon-App-40x40@3x.png
│ │ │ │ ├─ Icon-App-60x60@2x.png
│ │ │ │ ├─ Icon-App-60x60@3x.png
│ │ │ │ ├─ Icon-App-76x76@1x.png
│ │ │ │ ├─ Icon-App-76x76@2x.png
│ │ │ │ └─ Icon-App-83.5x83.5@2x.png
│ │ │ └─ LaunchImage.imageset
│ │ │ ├─ Contents.json
│ │ │ ├─ LaunchImage.png
│ │ │ ├─ LaunchImage@2x.png
│ │ │ ├─ LaunchImage@3x.png
│ │ │ └─ README.md
│ │ ├─ Base.lproj
│ │ │ ├─ LaunchScreen.storyboard
│ │ │ └─ Main.storyboard
│ │ ├─ GeneratedPluginRegistrant.h
│ │ ├─ GeneratedPluginRegistrant.m
│ │ ├─ Info.plist
│ │ └─ Runner-Bridging-Header.h
│ ├─ Runner.xcodeproj
│ │ ├─ project.pbxproj
│ │ ├─ project.xcworkspace
│ │ │ ├─ contents.xcworkspacedata
│ │ │ └─ xcshareddata
│ │ │ ├─ IDEWorkspaceChecks.plist
│ │ │ └─ WorkspaceSettings.xcsettings
│ │ └─ xcshareddata
│ │ └─ xcschemes
│ │ └─ Runner.xcscheme
│ └─ Runner.xcworkspace
│ ├─ contents.xcworkspacedata
│ └─ xcshareddata
│ ├─ IDEWorkspaceChecks.plist
│ └─ WorkspaceSettings.xcsettings
├─ lib
│ ├─ common
│ │ ├─ extensions
│ │ │ └─ num_extension.dart
│ │ ├─ styles
│ │ │ ├─ colors.dart
│ │ │ └─ typography.dart
│ │ └─ widgets
│ │ ├─ common_button.dart
│ │ └─ common_text.dart
│ ├─ main.dart
│ ├─ model
│ │ ├─ evaluate
│ │ │ └─ evaluate_model.dart
│ │ ├─ global_config
│ │ │ ├─ app_config_model.dart
│ │ │ ├─ game_config_model.dart
│ │ │ ├─ global_config_model.dart
│ │ │ └─ web_config_model.dart
│ │ ├─ goods
│ │ │ ├─ goods_base_model.dart
│ │ │ └─ goods_detail_model.dart
│ │ ├─ order
│ │ │ ├─ order_base_model.dart
│ │ │ └─ order_detail_model.dart
│ │ ├─ user
│ │ │ ├─ user_base_model.dart
│ │ │ └─ user_detail_model.dart
│ │ └─ wish
│ │ ├─ wish_base_model.dart
│ │ └─ wish_detail_model.dart
│ ├─ module
│ │ ├─ auth
│ │ │ ├─ pages
│ │ │ │ ├─ login_page
│ │ │ │ │ └─ login_page.dart
│ │ │ │ └─ register_page
│ │ │ │ └─ register_page.dart
│ │ │ ├─ routes
│ │ │ │ ├─ auth_route_handel.dart
│ │ │ │ ├─ auth_route_interceptor.dart
│ │ │ │ └─ auth_route_name.dart
│ │ │ └─ widgets
│ │ │ ├─ code_input_widget.dart
│ │ │ ├─ name_input_widget.dart
│ │ │ └─ tel_input_widget.dart
│ │ ├─ game
│ │ │ ├─ routes
│ │ │ │ ├─ game_route_handle.dart
│ │ │ │ ├─ game_route_interceptor.dart
│ │ │ │ └─ game_route_name.dart
│ │ │ └─ widgets
│ │ ├─ mall
│ │ │ ├─ pages
│ │ │ │ └─ mall_home_page
│ │ │ │ └─ mall_home_page.dart
│ │ │ ├─ routes
│ │ │ │ └─ mall_route_handle.dart
│ │ │ └─ widgets
│ │ ├─ order
│ │ │ ├─ pages
│ │ │ ├─ routes
│ │ │ └─ widgets
│ │ ├─ profile
│ │ │ ├─ pages
│ │ │ │ └─ profile_page
│ │ │ │ └─ profile_page.dart
│ │ │ └─ widgets
│ │ │ └─ profile_avatar.dart
│ │ ├─ web
│ │ │ └─ routes
│ │ │ ├─ web_route_handel.dart
│ │ │ ├─ web_route_interceptor.dart
│ │ │ └─ web_route_name.dart
│ │ └─ wish
│ │ ├─ pages
│ │ │ ├─ wish_create_add_sku_page
│ │ │ │ └─ wish_create_add_sku_page.dart
│ │ │ ├─ wish_create_page
│ │ │ │ └─ wish_ceate_page.dart
│ │ │ ├─ wish_detail_dark_page
│ │ │ │ └─ wish_detail_dark_page.dart
│ │ │ ├─ wish_detail_light_page
│ │ │ │ └─ wish_detail_light_page.dart
│ │ │ ├─ wish_goods_control_page
│ │ │ │ └─ wish_goods_control_page.dart
│ │ │ └─ wish_home_page
│ │ │ └─ wish_home_page.dart
│ │ └─ widgets
│ │ ├─ wish_create_add_sku_widget1.dart
│ │ └─ wish_create_add_sku_widget2.dart
│ ├─ service
│ │ ├─ global_config
│ │ │ ├─ cache
│ │ │ │ └─ global_config_cache.dart
│ │ │ ├─ manger
│ │ │ │ └─ global_config_service.dart
│ │ │ └─ request
│ │ │ └─ global_config_request.dart
│ │ ├─ order
│ │ │ ├─ cache
│ │ │ │ └─ order_cache.dart
│ │ │ ├─ manager
│ │ │ │ └─ order_service.dart
│ │ │ └─ request
│ │ │ └─ order_request.dart
│ │ ├─ publish
│ │ │ ├─ cache
│ │ │ │ └─ publish_cache.dart
│ │ │ ├─ manager
│ │ │ │ └─ publish_service.dart
│ │ │ └─ request
│ │ │ └─ publish_request.dart
│ │ └─ user
│ │ ├─ cache
│ │ │ └─ user_info_cache.dart
│ │ ├─ manager
│ │ │ └─ user_service.dart
│ │ └─ request
│ │ └─ user_info_request.dart
│ └─ util
│ └─ textsize_util.dart
├─ linux
│ ├─ .gitignore
│ ├─ CMakeLists.txt
│ ├─ flutter
│ │ ├─ CMakeLists.txt
│ │ ├─ generated_plugin_registrant.cc
│ │ ├─ generated_plugin_registrant.h
│ │ └─ generated_plugins.cmake
│ ├─ interceptors
│ │ ├─ game_interceptor.dart
│ │ └─ login_interceptor.dart
│ ├─ main.cc
│ ├─ my_application.cc
│ └─ my_application.h
├─ macos
│ ├─ .gitignore
│ ├─ Flutter
│ │ ├─ Flutter-Debug.xcconfig
│ │ ├─ Flutter-Release.xcconfig
│ │ ├─ GeneratedPluginRegistrant.swift
│ │ └─ ephemeral
│ │ ├─ Flutter-Generated.xcconfig
│ │ └─ flutter_export_environment.sh
│ ├─ Runner
│ │ ├─ AppDelegate.swift
│ │ ├─ Assets.xcassets
│ │ │ └─ AppIcon.appiconset
│ │ │ ├─ Contents.json
│ │ │ ├─ app_icon_1024.png
│ │ │ ├─ app_icon_128.png
│ │ │ ├─ app_icon_16.png
│ │ │ ├─ app_icon_256.png
│ │ │ ├─ app_icon_32.png
│ │ │ ├─ app_icon_512.png
│ │ │ └─ app_icon_64.png
│ │ ├─ Base.lproj
│ │ │ └─ MainMenu.xib
│ │ ├─ Configs
│ │ │ ├─ AppInfo.xcconfig
│ │ │ ├─ Debug.xcconfig
│ │ │ ├─ Release.xcconfig
│ │ │ └─ Warnings.xcconfig
│ │ ├─ DebugProfile.entitlements
│ │ ├─ Info.plist
│ │ ├─ MainFlutterWindow.swift
│ │ └─ Release.entitlements
│ ├─ Runner.xcodeproj
│ │ ├─ project.pbxproj
│ │ ├─ project.xcworkspace
│ │ │ └─ xcshareddata
│ │ │ └─ IDEWorkspaceChecks.plist
│ │ └─ xcshareddata
│ │ └─ xcschemes
│ │ └─ Runner.xcscheme
│ └─ Runner.xcworkspace
│ ├─ contents.xcworkspacedata
│ └─ xcshareddata
│ └─ IDEWorkspaceChecks.plist
├─ package
│ ├─ network
│ └─ route
│ ├─ errors
│ └─ manager
├─ pubspec.lock
├─ pubspec.yaml
├─ test
│ └─ widget_test.dart
├─ web
│ ├─ favicon.png
│ ├─ icons
│ │ ├─ Icon-192.png
│ │ ├─ Icon-512.png
│ │ ├─ Icon-maskable-192.png
│ │ └─ Icon-maskable-512.png
│ ├─ index.html
│ └─ manifest.json
└─ windows
├─ .gitignore
├─ CMakeLists.txt
├─ flutter
│ ├─ CMakeLists.txt
│ ├─ generated_plugin_registrant.cc
│ ├─ generated_plugin_registrant.h
│ └─ generated_plugins.cmake
└─ runner
├─ CMakeLists.txt
├─ Runner.rc
├─ flutter_window.cpp
├─ flutter_window.h
├─ main.cpp
├─ resource.h
├─ resources
│ └─ app_icon.ico
├─ runner.exe.manifest
├─ utils.cpp
├─ utils.h
├─ win32_window.cpp
└─ win32_window.h

END

好文分享: