14 files changed
+1010
-793
ios/Runner.xcodeproj/project.pbxproj
+69
-65
@@ -10,13 +10,13 @@
10
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
11
331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; };
12
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
13
- 593F905FB75129A384E40AED /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2F6AFCBB8B5168921CF0CF27 /* Pods_Runner.framework */; };
13
+ 4D35725BDD0DD5019A931DB2 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A032784C9BB81D3315A79076 /* Pods_RunnerTests.framework */; };
14
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
15
7884E8682EC3CC0700C636F2 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7884E8672EC3CC0400C636F2 /* SceneDelegate.swift */; };
16
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
17
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
18
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
19
- E426CC5FB0C84B59EA4C9455 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F3F7E264AC2540C04D71A2E0 /* Pods_RunnerTests.framework */; };
19
+ AD5D2C9F4682DFDBC0E7E5C1 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9B309EF20AB394872F65BD5E /* Pods_Runner.framework */; };
20
/* End PBXBuildFile section */
21
22
/* Begin PBXContainerItemProxy section */
@@ -45,9 +45,7 @@
45
/* Begin PBXFileReference section */
46
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
47
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
48
- 1E28B77AE84C180CE237C2CF /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = "<group>"; };
49
- 2910D1CEAE7EC5D481C55D03 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = "<group>"; };
50
- 2F6AFCBB8B5168921CF0CF27 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
48
+ 2E58D4AE73D0962B3323FB68 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
49
331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = "<group>"; };
50
331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
51
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
@@ -55,6 +53,8 @@
53
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
54
7884E8672EC3CC0400C636F2 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = "<group>"; };
55
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
56
+ 7CC868A42FD2D68B0007F1A7 /* Runner.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = "<group>"; };
57
+ 86D4F01C5ED86C95435B62E4 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = "<group>"; };
58
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
59
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
60
97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -62,11 +62,12 @@
62
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
63
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
64
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
65
- AA0C333EA96851D712658FE5 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = "<group>"; };
66
- AE6E886B14A635226D09D311 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
67
- C7571B6B9D4A156690DB988C /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
68
- F0DD11FD888D55FAA2DAFD54 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
69
- F3F7E264AC2540C04D71A2E0 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
65
+ 9B309EF20AB394872F65BD5E /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
66
+ A032784C9BB81D3315A79076 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
67
+ B01A7AB948A43BF5CF62AC78 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
68
+ D7F09850547E66B715D2AE1C /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = "<group>"; };
69
+ EB56E4A51F0E4D7E0193C01F /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
70
+ F63A76ABB8D4E03F11C4084E /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = "<group>"; };
71
/* End PBXFileReference section */
72
73
/* Begin PBXFrameworksBuildPhase section */
@@ -74,7 +75,7 @@
75
isa = PBXFrameworksBuildPhase;
76
buildActionMask = 2147483647;
77
files = (
77
- 593F905FB75129A384E40AED /* Pods_Runner.framework in Frameworks */,
78
+ AD5D2C9F4682DFDBC0E7E5C1 /* Pods_Runner.framework in Frameworks */,
79
);
80
runOnlyForDeploymentPostprocessing = 0;
81
};
@@ -82,13 +83,22 @@
83
isa = PBXFrameworksBuildPhase;
84
buildActionMask = 2147483647;
85
files = (
85
- E426CC5FB0C84B59EA4C9455 /* Pods_RunnerTests.framework in Frameworks */,
86
+ 4D35725BDD0DD5019A931DB2 /* Pods_RunnerTests.framework in Frameworks */,
87
);
88
runOnlyForDeploymentPostprocessing = 0;
89
};
90
/* End PBXFrameworksBuildPhase section */
91
92
/* Begin PBXGroup section */
93
+ 1149248C99B08ECDAC23FF9D /* Frameworks */ = {
94
+ isa = PBXGroup;
95
+ children = (
96
+ 9B309EF20AB394872F65BD5E /* Pods_Runner.framework */,
97
+ A032784C9BB81D3315A79076 /* Pods_RunnerTests.framework */,
98
+ );
99
+ name = Frameworks;
100
+ sourceTree = "<group>";
101
+ };
102
331C8082294A63A400263BE5 /* RunnerTests */ = {
103
isa = PBXGroup;
104
children = (
@@ -116,7 +126,7 @@
126
97C146EF1CF9000F007C117D /* Products */,
127
331C8082294A63A400263BE5 /* RunnerTests */,
128
B5F0F4932A555741EAC185FD /* Pods */,
119
- C9B81A28CEFDE833459BBAE4 /* Frameworks */,
129
+ 1149248C99B08ECDAC23FF9D /* Frameworks */,
130
);
131
sourceTree = "<group>";
132
};
@@ -132,6 +142,7 @@
142
97C146F01CF9000F007C117D /* Runner */ = {
143
isa = PBXGroup;
144
children = (
145
+ 7CC868A42FD2D68B0007F1A7 /* Runner.entitlements */,
146
97C146FA1CF9000F007C117D /* Main.storyboard */,
147
97C146FD1CF9000F007C117D /* Assets.xcassets */,
148
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
@@ -148,24 +159,14 @@
159
B5F0F4932A555741EAC185FD /* Pods */ = {
160
isa = PBXGroup;
161
children = (
151
- F0DD11FD888D55FAA2DAFD54 /* Pods-Runner.debug.xcconfig */,
152
- C7571B6B9D4A156690DB988C /* Pods-Runner.release.xcconfig */,
153
- AE6E886B14A635226D09D311 /* Pods-Runner.profile.xcconfig */,
154
- 1E28B77AE84C180CE237C2CF /* Pods-RunnerTests.debug.xcconfig */,
155
- AA0C333EA96851D712658FE5 /* Pods-RunnerTests.release.xcconfig */,
156
- 2910D1CEAE7EC5D481C55D03 /* Pods-RunnerTests.profile.xcconfig */,
157
- );
158
- name = Pods;
159
- path = Pods;
160
- sourceTree = "<group>";
161
- };
162
- C9B81A28CEFDE833459BBAE4 /* Frameworks */ = {
163
- isa = PBXGroup;
164
- children = (
165
- 2F6AFCBB8B5168921CF0CF27 /* Pods_Runner.framework */,
166
- F3F7E264AC2540C04D71A2E0 /* Pods_RunnerTests.framework */,
162
+ EB56E4A51F0E4D7E0193C01F /* Pods-Runner.debug.xcconfig */,
163
+ 2E58D4AE73D0962B3323FB68 /* Pods-Runner.release.xcconfig */,
164
+ B01A7AB948A43BF5CF62AC78 /* Pods-Runner.profile.xcconfig */,
165
+ F63A76ABB8D4E03F11C4084E /* Pods-RunnerTests.debug.xcconfig */,
166
+ D7F09850547E66B715D2AE1C /* Pods-RunnerTests.release.xcconfig */,
167
+ 86D4F01C5ED86C95435B62E4 /* Pods-RunnerTests.profile.xcconfig */,
168
);
168
- name = Frameworks;
169
+ path = Pods;
170
sourceTree = "<group>";
171
};
172
/* End PBXGroup section */
@@ -175,7 +176,7 @@
176
isa = PBXNativeTarget;
177
buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */;
178
buildPhases = (
178
- E66B1B93294383A7AD82BEE0 /* [CP] Check Pods Manifest.lock */,
179
+ D2249D2FD0465B2C76420590 /* [CP] Check Pods Manifest.lock */,
180
331C807D294A63A400263BE5 /* Sources */,
181
331C807F294A63A400263BE5 /* Resources */,
182
9AA24F4969F6FB2D761614D7 /* Frameworks */,
@@ -194,15 +195,15 @@
195
isa = PBXNativeTarget;
196
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
197
buildPhases = (
197
- AF1553571D404C2B52FA3EE8 /* [CP] Check Pods Manifest.lock */,
198
+ FE21A8B6035F35A6E6A38E92 /* [CP] Check Pods Manifest.lock */,
199
9740EEB61CF901F6004384FC /* Run Script */,
200
97C146EA1CF9000F007C117D /* Sources */,
201
97C146EB1CF9000F007C117D /* Frameworks */,
202
97C146EC1CF9000F007C117D /* Resources */,
203
9705A1C41CF9048500538489 /* Embed Frameworks */,
204
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
204
- 6F568F0CE167E332F9CAF0D6 /* [CP] Embed Pods Frameworks */,
205
- 91D3378898CA91FDC6BCE7FE /* [CP] Copy Pods Resources */,
205
+ 1E56B4EBA88B451F7B385BA9 /* [CP] Embed Pods Frameworks */,
206
+ D0F9B4752B25C3F455EAAB34 /* [CP] Copy Pods Resources */,
207
);
208
buildRules = (
209
);
@@ -274,6 +275,23 @@
275
/* End PBXResourcesBuildPhase section */
276
277
/* Begin PBXShellScriptBuildPhase section */
278
+ 1E56B4EBA88B451F7B385BA9 /* [CP] Embed Pods Frameworks */ = {
279
+ isa = PBXShellScriptBuildPhase;
280
+ buildActionMask = 2147483647;
281
+ files = (
282
+ );
283
+ inputFileListPaths = (
284
+ "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
285
+ );
286
+ name = "[CP] Embed Pods Frameworks";
287
+ outputFileListPaths = (
288
+ "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
289
+ );
290
+ runOnlyForDeploymentPostprocessing = 0;
291
+ shellPath = /bin/sh;
292
+ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
293
+ showEnvVarsInLog = 0;
294
+ };
295
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
296
isa = PBXShellScriptBuildPhase;
297
alwaysOutOfDate = 1;
@@ -290,24 +308,22 @@
308
shellPath = /bin/sh;
309
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
310
};
293
- 6F568F0CE167E332F9CAF0D6 /* [CP] Embed Pods Frameworks */ = {
311
+ 9740EEB61CF901F6004384FC /* Run Script */ = {
312
isa = PBXShellScriptBuildPhase;
313
+ alwaysOutOfDate = 1;
314
buildActionMask = 2147483647;
315
files = (
316
);
298
- inputFileListPaths = (
299
- "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
317
+ inputPaths = (
318
);
301
- name = "[CP] Embed Pods Frameworks";
302
- outputFileListPaths = (
303
- "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
319
+ name = "Run Script";
320
+ outputPaths = (
321
);
322
runOnlyForDeploymentPostprocessing = 0;
323
shellPath = /bin/sh;
307
- shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
308
- showEnvVarsInLog = 0;
324
+ shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
325
};
310
- 91D3378898CA91FDC6BCE7FE /* [CP] Copy Pods Resources */ = {
326
+ D0F9B4752B25C3F455EAAB34 /* [CP] Copy Pods Resources */ = {
327
isa = PBXShellScriptBuildPhase;
328
buildActionMask = 2147483647;
329
files = (
@@ -324,22 +340,7 @@
340
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n";
341
showEnvVarsInLog = 0;
342
};
327
- 9740EEB61CF901F6004384FC /* Run Script */ = {
328
- isa = PBXShellScriptBuildPhase;
329
- alwaysOutOfDate = 1;
330
- buildActionMask = 2147483647;
331
- files = (
332
- );
333
- inputPaths = (
334
- );
335
- name = "Run Script";
336
- outputPaths = (
337
- );
338
- runOnlyForDeploymentPostprocessing = 0;
339
- shellPath = /bin/sh;
340
- shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
341
- };
342
- AF1553571D404C2B52FA3EE8 /* [CP] Check Pods Manifest.lock */ = {
343
+ D2249D2FD0465B2C76420590 /* [CP] Check Pods Manifest.lock */ = {
344
isa = PBXShellScriptBuildPhase;
345
buildActionMask = 2147483647;
346
files = (
@@ -354,14 +355,14 @@
355
outputFileListPaths = (
356
);
357
outputPaths = (
357
- "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
358
+ "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt",
359
);
360
runOnlyForDeploymentPostprocessing = 0;
361
shellPath = /bin/sh;
362
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
363
showEnvVarsInLog = 0;
364
};
364
- E66B1B93294383A7AD82BEE0 /* [CP] Check Pods Manifest.lock */ = {
365
+ FE21A8B6035F35A6E6A38E92 /* [CP] Check Pods Manifest.lock */ = {
366
isa = PBXShellScriptBuildPhase;
367
buildActionMask = 2147483647;
368
files = (
@@ -376,7 +377,7 @@
377
outputFileListPaths = (
378
);
379
outputPaths = (
379
- "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt",
380
+ "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
381
);
382
runOnlyForDeploymentPostprocessing = 0;
383
shellPath = /bin/sh;
@@ -492,6 +493,7 @@
493
buildSettings = {
494
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
495
CLANG_ENABLE_MODULES = YES;
496
+ CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
497
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
498
DEVELOPMENT_TEAM = TZX5LG7ZW5;
499
ENABLE_BITCODE = NO;
@@ -510,7 +512,7 @@
512
};
513
331C8088294A63A400263BE5 /* Debug */ = {
514
isa = XCBuildConfiguration;
513
- baseConfigurationReference = 1E28B77AE84C180CE237C2CF /* Pods-RunnerTests.debug.xcconfig */;
515
+ baseConfigurationReference = F63A76ABB8D4E03F11C4084E /* Pods-RunnerTests.debug.xcconfig */;
516
buildSettings = {
517
BUNDLE_LOADER = "$(TEST_HOST)";
518
CODE_SIGN_STYLE = Automatic;
@@ -528,7 +530,7 @@
530
};
531
331C8089294A63A400263BE5 /* Release */ = {
532
isa = XCBuildConfiguration;
531
- baseConfigurationReference = AA0C333EA96851D712658FE5 /* Pods-RunnerTests.release.xcconfig */;
533
+ baseConfigurationReference = D7F09850547E66B715D2AE1C /* Pods-RunnerTests.release.xcconfig */;
534
buildSettings = {
535
BUNDLE_LOADER = "$(TEST_HOST)";
536
CODE_SIGN_STYLE = Automatic;
@@ -544,7 +546,7 @@
546
};
547
331C808A294A63A400263BE5 /* Profile */ = {
548
isa = XCBuildConfiguration;
547
- baseConfigurationReference = 2910D1CEAE7EC5D481C55D03 /* Pods-RunnerTests.profile.xcconfig */;
549
+ baseConfigurationReference = 86D4F01C5ED86C95435B62E4 /* Pods-RunnerTests.profile.xcconfig */;
550
buildSettings = {
551
BUNDLE_LOADER = "$(TEST_HOST)";
552
CODE_SIGN_STYLE = Automatic;
@@ -675,6 +677,7 @@
677
buildSettings = {
678
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
679
CLANG_ENABLE_MODULES = YES;
680
+ CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
681
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
682
DEVELOPMENT_TEAM = TZX5LG7ZW5;
683
ENABLE_BITCODE = NO;
@@ -698,6 +701,7 @@
701
buildSettings = {
702
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
703
CLANG_ENABLE_MODULES = YES;
704
+ CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
705
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
706
DEVELOPMENT_TEAM = TZX5LG7ZW5;
707
ENABLE_BITCODE = NO;
ios/Runner/Info.plist
+4
-1
@@ -76,12 +76,15 @@
76
<key>UIBackgroundModes</key>
77
<array>
78
<string>fetch</string>
79
- <string>processing</string>
79
<string>audio</string>
80
</array>
81
<key>UIFileSharingEnabled</key>
82
<true/>
83
<key>NSLocalNetworkUsageDescription</key>
84
<string>This app requires local network access for model inference services.</string>
85
+ <key>NSPhotoLibraryUsageDescription</key>
86
+ <string>This app requires access to your photo library to let you select and import files, documents, or images into your chats.</string>
87
+ <key>ITSAppUsesNonExemptEncryption</key>
88
+ <false/>
89
</dict>
90
</plist>
ios/Runner/Runner.entitlements
new
+8
@@ -0,0 +1,8 @@
1
+<?xml version="1.0" encoding="UTF-8"?>
2
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+<plist version="1.0">
4
+<dict>
5
+ <key>aps-environment</key>
6
+ <string>development</string>
7
+</dict>
8
+</plist>
lib/app.dart
+2
-2
@@ -58,12 +58,12 @@ final routerProvider = Provider<GoRouter>((ref) {
58
GoRoute(
59
path: AppRoutes.onboarding,
60
pageBuilder: (context, state) =>
61
- const NoTransitionPage(child: OnboardingLanguageScreen()),
61
+ const MaterialPage(child: OnboardingLanguageScreen()),
62
),
63
GoRoute(
64
path: AppRoutes.onboardingServerType,
65
pageBuilder: (context, state) =>
66
- const NoTransitionPage(child: OnboardingServerTypeScreen()),
66
+ const MaterialPage(child: OnboardingServerTypeScreen()),
67
),
68
GoRoute(
69
path: AppRoutes.onboardingSetup,
lib/features/models/screens/model_picker_sheet.dart
+4
-2
@@ -1,3 +1,5 @@
1
+import 'dart:io';
2
+
3
import 'package:flutter/material.dart';
4
import 'package:flutter_riverpod/flutter_riverpod.dart';
5
import 'package:localmind/l10n/app_localizations.dart';
@@ -837,7 +839,7 @@ class _OnDeviceModelTile extends ConsumerWidget {
839
),
840
],
841
),
840
- if (deviceMemory != null && deviceMemory!.isOversized(model.minRamMb))
842
+ if (!Platform.isIOS && deviceMemory != null && deviceMemory!.isOversized(model.minRamMb))
843
Padding(
844
padding: const EdgeInsets.only(top: 2),
845
child: Row(
@@ -980,7 +982,7 @@ class _OnDeviceModelTile extends ConsumerWidget {
982
}
983
984
void _loadModel(BuildContext context, WidgetRef ref) async {
983
- if (deviceMemory != null) {
985
+ if (!Platform.isIOS && deviceMemory != null) {
986
if (!deviceMemory!.hasEnoughRam(model.minRamMb)) {
987
final proceed = await _showRamWarning(context);
988
if (!proceed) return;
lib/features/on_device/views/model_manager_screen.dart
+5
-3
@@ -1,3 +1,5 @@
1
+import 'dart:io';
2
+
3
import 'package:flutter/material.dart';
4
import 'package:flutter_riverpod/flutter_riverpod.dart';
5
import 'package:localmind/l10n/app_localizations.dart';
@@ -460,7 +462,7 @@ class _ModelCard extends ConsumerWidget {
462
),
463
],
464
),
463
- if (deviceMemory != null && deviceMemory!.isOversized(model.minRamMb))
465
+ if (!Platform.isIOS && deviceMemory != null && deviceMemory!.isOversized(model.minRamMb))
466
Padding(
467
padding: const EdgeInsets.only(top: 6),
468
child: Container(
@@ -697,7 +699,7 @@ class _ModelCard extends ConsumerWidget {
699
700
Future<void> _startDownload(BuildContext context, WidgetRef ref) async {
701
final l10n = AppLocalizations.of(context)!;
700
- if (deviceMemory != null) {
702
+ if (!Platform.isIOS && deviceMemory != null) {
703
if (deviceMemory!.isOversized(model.minRamMb)) {
704
final proceed = await _showRamWarning(
705
context,
@@ -717,7 +719,7 @@ class _ModelCard extends ConsumerWidget {
719
720
Future<void> _loadModel(BuildContext context, WidgetRef ref) async {
721
final l10n = AppLocalizations.of(context)!;
720
- if (deviceMemory != null) {
722
+ if (!Platform.isIOS && deviceMemory != null) {
723
if (!deviceMemory!.hasEnoughRam(model.minRamMb)) {
724
final proceed = await _showRamWarning(
725
context,
lib/features/onboarding/screens/onboarding_language_screen.dart
+238
-209
@@ -3,6 +3,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
3
import 'package:go_router/go_router.dart';
4
import 'package:hugeicons/hugeicons.dart';
5
import 'package:shadcn_ui/shadcn_ui.dart';
6
+import 'package:cue/cue.dart';
7
8
import '../../../core/providers/app_providers.dart';
9
import '../../../core/routes/app_routes.dart';
@@ -174,237 +175,265 @@ class _OnboardingLanguageScreenState
175
),
176
],
177
),
177
- body: Column(
178
- crossAxisAlignment: CrossAxisAlignment.stretch,
179
- children: [
180
- Expanded(
181
- child: SingleChildScrollView(
182
- padding: const EdgeInsets.symmetric(horizontal: 24.0),
183
- child: Column(
184
- crossAxisAlignment: CrossAxisAlignment.stretch,
185
- children: [
186
- const SizedBox(height: 24),
187
- Text(
188
- l10n.onboarding_welcome,
189
- textAlign: TextAlign.center,
190
- style: theme.textTheme.headlineLarge?.copyWith(
191
- fontWeight: FontWeight.w800,
192
- height: 1.1,
193
- ),
194
- ),
195
- const SizedBox(height: 12),
196
- Text(
197
- l10n.onboarding_choose_language,
198
- textAlign: TextAlign.center,
199
- style: theme.textTheme.titleMedium?.copyWith(
200
- fontWeight: FontWeight.w600,
201
- color: theme.colorScheme.primary,
202
- ),
203
- ),
204
- const SizedBox(height: 8),
205
- Text(
206
- l10n.onboarding_choose_language_desc,
207
- textAlign: TextAlign.center,
208
- style: theme.textTheme.bodySmall?.copyWith(
209
- color: theme.colorScheme.onSurface.withValues(alpha: 0.7),
210
- height: 1.5,
211
- ),
212
- ),
213
- const SizedBox(height: 32),
214
- ListView.separated(
215
- shrinkWrap: true,
216
- physics: const NeverScrollableScrollPhysics(),
217
- itemCount: _languages.length,
218
- separatorBuilder: (context, index) => const SizedBox(height: 12),
219
- itemBuilder: (context, index) {
220
- final lang = _languages[index];
221
- final isSelected = _selectedCode == lang.code;
222
-
223
- return GestureDetector(
224
- onTap: () {
225
- setState(() {
226
- _selectedCode = lang.code;
227
- });
228
- // Dynamically apply settings localization update
229
- ref
230
- .read(settingsProvider.notifier)
231
- .setLocaleCode(lang.code);
232
- },
233
- child: AnimatedContainer(
234
- duration: const Duration(milliseconds: 200),
235
- padding: const EdgeInsets.symmetric(
236
- horizontal: 16,
237
- vertical: 12,
178
+ body: Cue.onMount(
179
+ motion: .smooth(),
180
+ child: Column(
181
+ crossAxisAlignment: CrossAxisAlignment.stretch,
182
+ children: [
183
+ Expanded(
184
+ child: SingleChildScrollView(
185
+ padding: const EdgeInsets.symmetric(horizontal: 24.0),
186
+ child: Column(
187
+ crossAxisAlignment: CrossAxisAlignment.stretch,
188
+ children: [
189
+ const SizedBox(height: 24),
190
+ Actor(
191
+ acts: [
192
+ .fadeIn(),
193
+ .slideY(from: 0.08),
194
+ ],
195
+ child: Column(
196
+ crossAxisAlignment: CrossAxisAlignment.stretch,
197
+ children: [
198
+ Text(
199
+ l10n.onboarding_welcome,
200
+ textAlign: TextAlign.center,
201
+ style: theme.textTheme.headlineLarge?.copyWith(
202
+ fontWeight: FontWeight.w800,
203
+ height: 1.1,
204
+ ),
205
),
239
- decoration: BoxDecoration(
240
- color: theme.colorScheme.surface,
241
- borderRadius: BorderRadius.circular(16),
242
- border: Border.all(
243
- color: isSelected
244
- ? theme.colorScheme.primary
245
- : theme.colorScheme.outline
246
- .withValues(alpha: 0.15),
247
- width: isSelected ? 2 : 1,
206
+ const SizedBox(height: 12),
207
+ Text(
208
+ l10n.onboarding_choose_language,
209
+ textAlign: TextAlign.center,
210
+ style: theme.textTheme.titleMedium?.copyWith(
211
+ fontWeight: FontWeight.w600,
212
+ color: theme.colorScheme.primary,
213
),
249
- boxShadow: isSelected
250
- ? [
251
- BoxShadow(
252
- color: theme.colorScheme.primary
253
- .withValues(alpha: 0.1),
254
- blurRadius: 8,
255
- offset: const Offset(0, 2),
256
- )
257
- ]
258
- : null,
214
),
260
- child: Row(
261
- children: [
262
- Container(
263
- width: 48,
264
- height: 32,
265
- decoration: BoxDecoration(
266
- color: Colors.white,
267
- borderRadius: BorderRadius.circular(6),
268
- border: Border.all(
269
- color: theme.colorScheme.outline
270
- .withValues(alpha: 0.15),
271
- width: 1,
272
- ),
273
- boxShadow: [
274
- BoxShadow(
275
- color: Colors.black.withValues(alpha: 0.05),
276
- blurRadius: 4,
277
- offset: const Offset(0, 2),
278
- ),
279
- ],
215
+ const SizedBox(height: 8),
216
+ Text(
217
+ l10n.onboarding_choose_language_desc,
218
+ textAlign: TextAlign.center,
219
+ style: theme.textTheme.bodySmall?.copyWith(
220
+ color: theme.colorScheme.onSurface.withValues(alpha: 0.7),
221
+ height: 1.5,
222
+ ),
223
+ ),
224
+ ],
225
+ ),
226
+ ),
227
+ const SizedBox(height: 32),
228
+ Actor(
229
+ delay: 60.ms,
230
+ acts: [
231
+ .fadeIn(),
232
+ .slideY(from: 0.08),
233
+ ],
234
+ child: ListView.separated(
235
+ shrinkWrap: true,
236
+ physics: const NeverScrollableScrollPhysics(),
237
+ itemCount: _languages.length,
238
+ separatorBuilder: (context, index) => const SizedBox(height: 12),
239
+ itemBuilder: (context, index) {
240
+ final lang = _languages[index];
241
+ final isSelected = _selectedCode == lang.code;
242
+
243
+ return GestureDetector(
244
+ onTap: () {
245
+ setState(() {
246
+ _selectedCode = lang.code;
247
+ });
248
+ // Dynamically apply settings localization update
249
+ ref
250
+ .read(settingsProvider.notifier)
251
+ .setLocaleCode(lang.code);
252
+ },
253
+ child: AnimatedContainer(
254
+ duration: const Duration(milliseconds: 200),
255
+ padding: const EdgeInsets.symmetric(
256
+ horizontal: 16,
257
+ vertical: 12,
258
+ ),
259
+ decoration: BoxDecoration(
260
+ color: theme.colorScheme.surface,
261
+ borderRadius: BorderRadius.circular(16),
262
+ border: Border.all(
263
+ color: isSelected
264
+ ? theme.colorScheme.primary
265
+ : theme.colorScheme.outline
266
+ .withValues(alpha: 0.15),
267
+ width: isSelected ? 2 : 1,
268
),
281
- alignment: Alignment.center,
282
- child: ClipRRect(
283
- borderRadius: BorderRadius.circular(5),
284
- child: Image.asset(
285
- lang.flagAsset,
269
+ boxShadow: isSelected
270
+ ? [
271
+ BoxShadow(
272
+ color: theme.colorScheme.primary
273
+ .withValues(alpha: 0.1),
274
+ blurRadius: 8,
275
+ offset: const Offset(0, 2),
276
+ )
277
+ ]
278
+ : null,
279
+ ),
280
+ child: Row(
281
+ children: [
282
+ Container(
283
width: 48,
284
height: 32,
288
- fit: BoxFit.cover,
289
- errorBuilder: (context, error, stackTrace) {
290
- return Text(
291
- lang.flag,
292
- style: const TextStyle(fontSize: 18),
293
- );
294
- },
295
- ),
296
- ),
297
- ),
298
- const SizedBox(width: 16),
299
- Expanded(
300
- child: Column(
301
- crossAxisAlignment: CrossAxisAlignment.start,
302
- mainAxisSize: MainAxisSize.min,
303
- children: [
304
- Text(
305
- lang.nativeName,
306
- style:
307
- theme.textTheme.titleMedium?.copyWith(
308
- fontWeight: FontWeight.bold,
285
+ decoration: BoxDecoration(
286
+ color: Colors.white,
287
+ borderRadius: BorderRadius.circular(6),
288
+ border: Border.all(
289
+ color: theme.colorScheme.outline
290
+ .withValues(alpha: 0.15),
291
+ width: 1,
292
),
293
+ boxShadow: [
294
+ BoxShadow(
295
+ color: Colors.black.withValues(alpha: 0.05),
296
+ blurRadius: 4,
297
+ offset: const Offset(0, 2),
298
+ ),
299
+ ],
300
),
311
- const SizedBox(height: 2),
312
- Text(
313
- '${lang.englishName} • ${lang.shortText.toUpperCase()}',
314
- style: theme.textTheme.labelSmall?.copyWith(
315
- color: theme.colorScheme.onSurface
316
- .withValues(alpha: 0.5),
301
+ alignment: Alignment.center,
302
+ child: ClipRRect(
303
+ borderRadius: BorderRadius.circular(5),
304
+ child: Image.asset(
305
+ lang.flagAsset,
306
+ width: 48,
307
+ height: 32,
308
+ fit: BoxFit.cover,
309
+ errorBuilder: (context, error, stackTrace) {
310
+ return Text(
311
+ lang.flag,
312
+ style: const TextStyle(fontSize: 18),
313
+ );
314
+ },
315
),
316
),
319
- ],
320
- ),
321
- ),
322
- const SizedBox(width: 8),
323
- AnimatedContainer(
324
- duration: const Duration(milliseconds: 200),
325
- width: 22,
326
- height: 22,
327
- decoration: BoxDecoration(
328
- shape: BoxShape.circle,
329
- color: isSelected
330
- ? theme.colorScheme.primary
331
- : Colors.transparent,
332
- border: Border.all(
333
- color: isSelected
334
- ? theme.colorScheme.primary
335
- : theme.colorScheme.outline
336
- .withValues(alpha: 0.3),
337
- width: 2,
317
),
339
- ),
340
- child: isSelected
341
- ? const Icon(
342
- Icons.check,
343
- color: Colors.white,
344
- size: 14,
345
- )
346
- : null,
318
+ const SizedBox(width: 16),
319
+ Expanded(
320
+ child: Column(
321
+ crossAxisAlignment: CrossAxisAlignment.start,
322
+ mainAxisSize: MainAxisSize.min,
323
+ children: [
324
+ Text(
325
+ lang.nativeName,
326
+ style:
327
+ theme.textTheme.titleMedium?.copyWith(
328
+ fontWeight: FontWeight.bold,
329
+ ),
330
+ ),
331
+ const SizedBox(height: 2),
332
+ Text(
333
+ '${lang.englishName} • ${lang.shortText.toUpperCase()}',
334
+ style: theme.textTheme.labelSmall?.copyWith(
335
+ color: theme.colorScheme.onSurface
336
+ .withValues(alpha: 0.5),
337
+ ),
338
+ ),
339
+ ],
340
+ ),
341
+ ),
342
+ const SizedBox(width: 8),
343
+ AnimatedContainer(
344
+ duration: const Duration(milliseconds: 200),
345
+ width: 22,
346
+ height: 22,
347
+ decoration: BoxDecoration(
348
+ shape: BoxShape.circle,
349
+ color: isSelected
350
+ ? theme.colorScheme.primary
351
+ : Colors.transparent,
352
+ border: Border.all(
353
+ color: isSelected
354
+ ? theme.colorScheme.primary
355
+ : theme.colorScheme.outline
356
+ .withValues(alpha: 0.3),
357
+ width: 2,
358
+ ),
359
+ ),
360
+ child: isSelected
361
+ ? const Icon(
362
+ Icons.check,
363
+ color: Colors.white,
364
+ size: 14,
365
+ )
366
+ : null,
367
+ ),
368
+ ],
369
),
348
- ],
349
- ),
350
- ),
351
- );
352
- },
353
- ),
354
- const SizedBox(height: 32),
355
- ],
370
+ ),
371
+ );
372
+ },
373
+ ),
374
+ ),
375
+ const SizedBox(height: 32),
376
+ ],
377
+ ),
378
),
379
),
358
- ),
359
- Padding(
360
- padding: const EdgeInsets.symmetric(horizontal: 24.0),
361
- child: Column(
362
- children: [
363
- Row(
364
- mainAxisAlignment: MainAxisAlignment.center,
380
+ Actor(
381
+ delay: 120.ms,
382
+ acts: [
383
+ .fadeIn(),
384
+ .slideY(from: 0.08),
385
+ ],
386
+ child: Padding(
387
+ padding: const EdgeInsets.symmetric(horizontal: 24.0),
388
+ child: Column(
389
children: [
366
- Container(
367
- width: 8,
368
- height: 8,
369
- decoration: BoxDecoration(
370
- color: theme.colorScheme.primary,
371
- shape: BoxShape.circle,
372
- ),
390
+ Row(
391
+ mainAxisAlignment: MainAxisAlignment.center,
392
+ children: [
393
+ Container(
394
+ width: 8,
395
+ height: 8,
396
+ decoration: BoxDecoration(
397
+ color: theme.colorScheme.primary,
398
+ shape: BoxShape.circle,
399
+ ),
400
+ ),
401
+ const SizedBox(width: 8),
402
+ Text(
403
+ l10n.ready_continue,
404
+ style: theme.textTheme.labelSmall?.copyWith(
405
+ color: theme.colorScheme.onSurface.withValues(
406
+ alpha: 0.5,
407
+ ),
408
+ letterSpacing: 2,
409
+ fontWeight: FontWeight.w600,
410
+ ),
411
+ ),
412
+ ],
413
),
374
- const SizedBox(width: 8),
375
- Text(
376
- l10n.ready_continue,
377
- style: theme.textTheme.labelSmall?.copyWith(
378
- color: theme.colorScheme.onSurface.withValues(
379
- alpha: 0.5,
414
+ const SizedBox(height: 16),
415
+ ShadButton(
416
+ width: double.infinity,
417
+ onPressed: () {
418
+ context.push(AppRoutes.onboardingServerType);
419
+ },
420
+ child: Text(
421
+ l10n.continue_action,
422
+ style: const TextStyle(
423
+ fontSize: 16,
424
+ fontWeight: FontWeight.w600,
425
),
381
- letterSpacing: 2,
382
- fontWeight: FontWeight.w600,
426
),
427
),
385
- ],
386
- ),
387
- const SizedBox(height: 16),
388
- ShadButton(
389
- width: double.infinity,
390
- onPressed: () {
391
- context.push(AppRoutes.onboardingServerType);
392
- },
393
- child: Text(
394
- l10n.continue_action,
395
- style: const TextStyle(
396
- fontSize: 16,
397
- fontWeight: FontWeight.w600,
428
+ SizedBox(
429
+ height: MediaQuery.of(context).padding.bottom,
430
),
399
- ),
400
- ),
401
- SizedBox(
402
- height: MediaQuery.of(context).padding.bottom,
431
+ ],
432
),
404
- ],
433
+ ),
434
),
406
- ),
407
- ],
435
+ ],
436
+ ),
437
),
438
);
439
}
lib/features/onboarding/screens/onboarding_model_download_screen.dart
+94
-58
@@ -5,6 +5,7 @@ import 'package:flutter/material.dart';
5
import 'package:flutter_riverpod/flutter_riverpod.dart';
6
import 'package:go_router/go_router.dart';
7
import 'package:shadcn_ui/shadcn_ui.dart';
8
+import 'package:cue/cue.dart';
9
10
import '../../../core/models/enums.dart';
11
import '../../../core/routes/app_routes.dart';
@@ -38,77 +39,112 @@ class _OnboardingModelDownloadScreenState
39
final models = ref.watch(onDeviceModelsProvider);
40
final downloadedModelsAsync = ref.watch(downloadedModelsProvider);
41
final downloadStates = ref.watch(foregroundDownloadNotifierProvider);
41
- final isAndroid = Platform.isAndroid;
42
final deviceMemoryAsync = ref.watch(deviceMemoryProvider);
43
44
return Scaffold(
45
appBar: AppBar(title: Text(l10n.download_model_title)),
46
body: SafeArea(
47
- child: ListView(
48
- padding: const EdgeInsets.symmetric(horizontal: 24.0, vertical: 16.0),
49
- children: [
50
- Text(
51
- l10n.download_model_desc,
52
- style: theme.textTheme.bodyMedium?.copyWith(
53
- color: theme.colorScheme.onSurface.withValues(alpha: 0.7),
54
- ),
55
- ),
56
- const SizedBox(height: 24),
57
- if (!isAndroid)
58
- Container(
59
- padding: const EdgeInsets.all(12),
60
- margin: const EdgeInsets.only(bottom: 16),
61
- decoration: BoxDecoration(
62
- color: Colors.orange.withValues(alpha: 0.1),
63
- borderRadius: BorderRadius.circular(8),
64
- border: Border.all(color: Colors.orange),
65
- ),
66
- child: Row(
47
+ child: Cue.onMount(
48
+ motion: .smooth(),
49
+ child: ListView(
50
+ padding: const EdgeInsets.symmetric(horizontal: 24.0, vertical: 16.0),
51
+ children: [
52
+ Actor(
53
+ acts: [
54
+ .fadeIn(),
55
+ .slideY(from: 0.08),
56
+ ],
57
+ child: Column(
58
+ crossAxisAlignment: CrossAxisAlignment.stretch,
59
children: [
68
- const Icon(Icons.info_outline, color: Colors.orange, size: 20),
69
- const SizedBox(width: 8),
70
- Expanded(
71
- child: Text(
72
- l10n.on_device_android_only,
73
- style: const TextStyle(color: Colors.orange),
60
+ Text(
61
+ l10n.download_model_desc,
62
+ style: theme.textTheme.bodyMedium?.copyWith(
63
+ color: theme.colorScheme.onSurface.withValues(alpha: 0.7),
64
),
65
),
66
+ const SizedBox(height: 24),
67
+ if (!Platform.isAndroid && !Platform.isIOS)
68
+ Container(
69
+ padding: const EdgeInsets.all(12),
70
+ margin: const EdgeInsets.only(bottom: 16),
71
+ decoration: BoxDecoration(
72
+ color: Colors.orange.withValues(alpha: 0.1),
73
+ borderRadius: BorderRadius.circular(8),
74
+ border: Border.all(color: Colors.orange),
75
+ ),
76
+ child: Row(
77
+ children: [
78
+ const Icon(Icons.info_outline, color: Colors.orange, size: 20),
79
+ const SizedBox(width: 8),
80
+ Expanded(
81
+ child: Text(
82
+ l10n.on_device_android_only,
83
+ style: const TextStyle(color: Colors.orange),
84
+ ),
85
+ ),
86
+ ],
87
+ ),
88
+ ),
89
+ _buildMemoryInfo(context, ref, deviceMemoryAsync),
90
],
91
),
92
),
79
- _buildMemoryInfo(context, ref, deviceMemoryAsync),
80
- ...models.map((model) {
81
- final isDownloaded = downloadedModelsAsync.when(
82
- data: (set) => set.contains(model.id),
83
- loading: () => false,
84
- error: (_, _) => false,
85
- );
86
- final progressInfo = downloadStates[model.id];
93
+ Actor(
94
+ delay: 60.ms,
95
+ acts: [
96
+ .fadeIn(),
97
+ .slideY(from: 0.08),
98
+ ],
99
+ child: Column(
100
+ crossAxisAlignment: CrossAxisAlignment.stretch,
101
+ children: models.map((model) {
102
+ final isDownloaded = downloadedModelsAsync.when(
103
+ data: (set) => set.contains(model.id),
104
+ loading: () => false,
105
+ error: (_, _) => false,
106
+ );
107
+ final progressInfo = downloadStates[model.id];
108
88
- return Padding(
89
- padding: const EdgeInsets.only(bottom: 12),
90
- child: _ModelCard(
91
- model: model,
92
- isDownloaded: isDownloaded,
93
- progressInfo: progressInfo,
94
- theme: theme,
95
- deviceMemory: deviceMemoryAsync.value,
109
+ return Padding(
110
+ padding: const EdgeInsets.only(bottom: 12),
111
+ child: _ModelCard(
112
+ model: model,
113
+ isDownloaded: isDownloaded,
114
+ progressInfo: progressInfo,
115
+ theme: theme,
116
+ deviceMemory: deviceMemoryAsync.value,
117
+ ),
118
+ );
119
+ }).toList(),
120
),
97
- );
98
- }),
99
- const SizedBox(height: 24),
100
- if (_isCreatingServer)
101
- const Center(child: CircularProgressIndicator())
102
- else
103
- ShadButton(
104
- width: double.infinity,
105
- onPressed: _canContinue() ? _createOnDeviceServer : null,
106
- child: Text(
107
- l10n.continue_action,
108
- style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w600),
121
+ ),
122
+ Actor(
123
+ delay: 120.ms,
124
+ acts: [
125
+ .fadeIn(),
126
+ .slideY(from: 0.08),
127
+ ],
128
+ child: Column(
129
+ crossAxisAlignment: CrossAxisAlignment.stretch,
130
+ children: [
131
+ const SizedBox(height: 24),
132
+ if (_isCreatingServer)
133
+ const Center(child: CircularProgressIndicator())
134
+ else
135
+ ShadButton(
136
+ width: double.infinity,
137
+ onPressed: _canContinue() ? _createOnDeviceServer : null,
138
+ child: Text(
139
+ l10n.continue_action,
140
+ style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w600),
141
+ ),
142
+ ),
143
+ ],
144
),
145
),
111
- ],
146
+ ],
147
+ ),
148
),
149
),
150
);
@@ -311,7 +347,7 @@ class _ModelCard extends ConsumerWidget {
347
),
348
],
349
),
314
- if (deviceMemory != null && deviceMemory!.isOversized(model.minRamMb))
350
+ if (!Platform.isIOS && deviceMemory != null && deviceMemory!.isOversized(model.minRamMb))
351
Padding(
352
padding: const EdgeInsets.only(top: 4),
353
child: Row(
@@ -438,7 +474,7 @@ class _ModelCard extends ConsumerWidget {
474
}
475
476
Future<void> _startDownload(BuildContext context, WidgetRef ref) async {
441
- if (deviceMemory != null) {
477
+ if (!Platform.isIOS && deviceMemory != null) {
478
if (deviceMemory!.isOversized(model.minRamMb)) {
479
final proceed = await _showRamWarning(context);
480
if (!proceed) return;
lib/features/onboarding/screens/onboarding_notification_permission_screen.dart
+125
-86
@@ -3,6 +3,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
3
import 'package:go_router/go_router.dart';
4
import 'package:hugeicons/hugeicons.dart';
5
import 'package:shadcn_ui/shadcn_ui.dart';
6
+import 'package:cue/cue.dart';
7
8
import '../../../core/providers/app_providers.dart';
9
import '../../../core/routes/app_routes.dart';
@@ -57,101 +58,139 @@ class _OnboardingNotificationPermissionScreenState
58
59
return Scaffold(
60
body: SafeArea(
60
- child: Padding(
61
- padding: const EdgeInsets.symmetric(horizontal: 24.0, vertical: 32.0),
62
- child: Column(
63
- crossAxisAlignment: CrossAxisAlignment.center,
64
- children: [
65
- const Spacer(),
66
- // Visual element
67
- Container(
68
- padding: const EdgeInsets.all(32),
69
- decoration: BoxDecoration(
70
- color: theme.colorScheme.primaryContainer.withValues(
71
- alpha: 0.2,
61
+ child: Cue.onMount(
62
+ motion: .smooth(),
63
+ child: Padding(
64
+ padding: const EdgeInsets.symmetric(horizontal: 24.0, vertical: 32.0),
65
+ child: Column(
66
+ crossAxisAlignment: CrossAxisAlignment.center,
67
+ children: [
68
+ const Spacer(),
69
+ // Visual element
70
+ Actor(
71
+ acts: [
72
+ .fadeIn(),
73
+ .scale(from: 0.8),
74
+ ],
75
+ child: Container(
76
+ padding: const EdgeInsets.all(32),
77
+ decoration: BoxDecoration(
78
+ color: theme.colorScheme.primaryContainer.withValues(
79
+ alpha: 0.2,
80
+ ),
81
+ shape: BoxShape.circle,
82
+ ),
83
+ child: HugeIcon(
84
+ icon: HugeIcons.strokeRoundedNotification03,
85
+ size: 80,
86
+ color: theme.colorScheme.primary,
87
+ ),
88
),
73
- shape: BoxShape.circle,
89
),
75
- child: HugeIcon(
76
- icon: HugeIcons.strokeRoundedNotification03,
77
- size: 80,
78
- color: theme.colorScheme.primary,
79
- ),
80
- ),
81
- const SizedBox(height: 48),
82
- Text(
83
- l10n.stay_updated,
84
- style: theme.textTheme.displaySmall?.copyWith(
85
- fontWeight: FontWeight.bold,
90
+ const SizedBox(height: 48),
91
+ Actor(
92
+ delay: 60.ms,
93
+ acts: [
94
+ .fadeIn(),
95
+ .slideY(from: 0.08),
96
+ ],
97
+ child: Column(
98
+ children: [
99
+ Text(
100
+ l10n.stay_updated,
101
+ style: theme.textTheme.displaySmall?.copyWith(
102
+ fontWeight: FontWeight.bold,
103
+ ),
104
+ textAlign: TextAlign.center,
105
+ ),
106
+ const SizedBox(height: 16),
107
+ Text(
108
+ l10n.stay_updated_desc,
109
+ style: theme.textTheme.titleMedium?.copyWith(
110
+ color: theme.colorScheme.onSurface.withValues(alpha: 0.7),
111
+ ),
112
+ textAlign: TextAlign.center,
113
+ ),
114
+ ],
115
+ ),
116
),
87
- textAlign: TextAlign.center,
88
- ),
89
- const SizedBox(height: 16),
90
- Text(
91
- l10n.stay_updated_desc,
92
- style: theme.textTheme.titleMedium?.copyWith(
93
- color: theme.colorScheme.onSurface.withValues(alpha: 0.7),
117
+ const SizedBox(height: 48),
118
+ // Benefits list
119
+ Actor(
120
+ delay: 120.ms,
121
+ acts: [
122
+ .fadeIn(),
123
+ .slideY(from: 0.08),
124
+ ],
125
+ child: Column(
126
+ children: [
127
+ _buildBenefitRow(
128
+ context,
129
+ HugeIcons.strokeRoundedDownload01,
130
+ l10n.notification_benefit_downloads,
131
+ ),
132
+ const SizedBox(height: 16),
133
+ _buildBenefitRow(
134
+ context,
135
+ HugeIcons.strokeRoundedAiChat01,
136
+ l10n.notification_benefit_completions,
137
+ ),
138
+ const SizedBox(height: 16),
139
+ _buildBenefitRow(
140
+ context,
141
+ HugeIcons.strokeRoundedClock01,
142
+ l10n.notification_benefit_background,
143
+ ),
144
+ ],
145
+ ),
146
),
95
- textAlign: TextAlign.center,
96
- ),
97
- const SizedBox(height: 48),
98
- // Benefits list
99
- _buildBenefitRow(
100
- context,
101
- HugeIcons.strokeRoundedDownload01,
102
- l10n.notification_benefit_downloads,
103
- ),
104
- const SizedBox(height: 16),
105
- _buildBenefitRow(
106
- context,
107
- HugeIcons.strokeRoundedAiChat01,
108
- l10n.notification_benefit_completions,
109
- ),
110
- const SizedBox(height: 16),
111
- _buildBenefitRow(
112
- context,
113
- HugeIcons.strokeRoundedClock01,
114
- l10n.notification_benefit_background,
115
- ),
116
- const Spacer(),
117
- const SizedBox(height: 32),
118
- Column(
119
- crossAxisAlignment: CrossAxisAlignment.stretch,
120
- children: [
121
- ShadButton(
122
- onPressed: _isProcessing ? null : _requestPermission,
123
- child: _isProcessing
124
- ? const SizedBox(
125
- height: 20,
126
- width: 20,
127
- child: CircularProgressIndicator(
128
- strokeWidth: 2,
129
- valueColor: AlwaysStoppedAnimation(Colors.white),
130
- ),
131
- )
132
- : Text(
133
- l10n.allow_notifications,
134
- style: const TextStyle(
135
- fontSize: 16,
136
- fontWeight: FontWeight.w600,
147
+ const Spacer(),
148
+ const SizedBox(height: 32),
149
+ Actor(
150
+ delay: 180.ms,
151
+ acts: [
152
+ .fadeIn(),
153
+ .slideY(from: 0.08),
154
+ ],
155
+ child: Column(
156
+ crossAxisAlignment: CrossAxisAlignment.stretch,
157
+ children: [
158
+ ShadButton(
159
+ onPressed: _isProcessing ? null : _requestPermission,
160
+ child: _isProcessing
161
+ ? const SizedBox(
162
+ height: 20,
163
+ width: 20,
164
+ child: CircularProgressIndicator(
165
+ strokeWidth: 2,
166
+ valueColor: AlwaysStoppedAnimation(Colors.white),
167
+ ),
168
+ )
169
+ : Text(
170
+ l10n.allow_notifications,
171
+ style: const TextStyle(
172
+ fontSize: 16,
173
+ fontWeight: FontWeight.w600,
174
+ ),
175
+ ),
176
+ ),
177
+ const SizedBox(height: 12),
178
+ ShadButton.ghost(
179
+ onPressed: _isProcessing ? null : _completeOnboarding,
180
+ child: Text(
181
+ l10n.not_now,
182
+ style: TextStyle(
183
+ color: theme.colorScheme.onSurface.withValues(
184
+ alpha: 0.6,
185
),
186
),
139
- ),
140
- const SizedBox(height: 12),
141
- ShadButton.ghost(
142
- onPressed: _isProcessing ? null : _completeOnboarding,
143
- child: Text(
144
- l10n.not_now,
145
- style: TextStyle(
146
- color: theme.colorScheme.onSurface.withValues(
147
- alpha: 0.6,
187
),
188
),
150
- ),
189
+ ],
190
),
152
- ],
153
- ),
154
- ],
191
+ ),
192
+ ],
193
+ ),
194
),
195
),
196
),
lib/features/onboarding/screens/onboarding_server_setup_screen.dart
+164
-137
@@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
4
import 'package:flutter_riverpod/flutter_riverpod.dart';
5
import 'package:go_router/go_router.dart';
6
import 'package:shadcn_ui/shadcn_ui.dart';
7
+import 'package:cue/cue.dart';
8
9
import '../../../core/constants/app_constants.dart';
10
import '../../../core/models/enums.dart';
@@ -181,156 +182,182 @@ class _OnboardingServerSetupScreenState
182
return Scaffold(
183
appBar: AppBar(title: Text(l10n.setup_connection)),
184
body: SafeArea(
184
- child: Form(
185
- key: _formKey,
186
- child: ListView(
187
- padding: const EdgeInsets.symmetric(
188
- horizontal: 24.0,
189
- vertical: 16.0,
190
- ),
191
- children: [
192
- Text(
193
- l10n.setup_connection_desc(widget.selectedType.name),
194
- style: theme.textTheme.titleMedium?.copyWith(
195
- color: theme.colorScheme.onSurface.withValues(alpha: 0.7),
196
- ),
197
- ),
198
- const SizedBox(height: 32),
199
-
200
- TextFormField(
201
- controller: _nameController,
202
- decoration: InputDecoration(
203
- labelText: l10n.server_name,
204
- filled: true,
205
- fillColor: theme.colorScheme.surface,
206
- ),
207
- validator: (val) => val == null || val.trim().isEmpty
208
- ? l10n.name_required
209
- : null,
210
- ),
211
- const SizedBox(height: 16),
212
-
213
- if (!isCloud) ...[
214
- TextFormField(
215
- controller: _hostController,
216
- decoration: InputDecoration(
217
- labelText: l10n.host_label,
218
- filled: true,
219
- fillColor: theme.colorScheme.surface,
220
- ),
221
- validator: (val) => val == null || val.trim().isEmpty
222
- ? l10n.host_required
223
- : null,
224
- ),
225
- const SizedBox(height: 16),
226
-
227
- TextFormField(
228
- controller: _portController,
229
- decoration: InputDecoration(
230
- labelText: l10n.port_label,
231
- filled: true,
232
- fillColor: theme.colorScheme.surface,
233
- ),
234
- validator: (val) {
235
- if (val == null || val.trim().isEmpty) {
236
- return l10n.port_required;
237
- } else {
238
- if (int.tryParse(val) == null) {
239
- return l10n.port_invalid;
240
- } else {
241
- return null;
242
- }
243
- }
244
- },
245
- ),
246
- const SizedBox(height: 16),
247
- ],
248
-
249
- TextFormField(
250
- controller: _apiKeyController,
251
- decoration: InputDecoration(
252
- labelText: isCloud
253
- ? l10n.api_key_required
254
- : l10n.api_key_optional,
255
- filled: true,
256
- fillColor: theme.colorScheme.surface,
257
- ),
258
- validator: (val) {
259
- if (isCloud && (val == null || val.trim().isEmpty)) {
260
- return l10n.api_key_required_openrouter;
261
- }
262
- return null;
263
- },
264
- obscureText: true,
185
+ child: Cue.onMount(
186
+ motion: .smooth(),
187
+ child: Form(
188
+ key: _formKey,
189
+ child: ListView(
190
+ padding: const EdgeInsets.symmetric(
191
+ horizontal: 24.0,
192
+ vertical: 16.0,
193
),
266
- const SizedBox(height: 24),
267
-
268
- if (_testResult != null)
269
- Container(
270
- padding: const EdgeInsets.all(12),
271
- margin: const EdgeInsets.only(bottom: 24),
272
- decoration: BoxDecoration(
273
- color: _testSuccess
274
- ? Colors.green.withValues(alpha: 0.1)
275
- : Colors.red.withValues(alpha: 0.1),
276
- borderRadius: BorderRadius.circular(8),
277
- border: Border.all(
278
- color: _testSuccess ? Colors.green : Colors.red,
194
+ children: [
195
+ Actor(
196
+ acts: [
197
+ .fadeIn(),
198
+ .slideY(from: 0.08),
199
+ ],
200
+ child: Text(
201
+ l10n.setup_connection_desc(widget.selectedType.name),
202
+ style: theme.textTheme.titleMedium?.copyWith(
203
+ color: theme.colorScheme.onSurface.withValues(alpha: 0.7),
204
),
205
),
281
- child: Row(
206
+ ),
207
+ Actor(
208
+ delay: 60.ms,
209
+ acts: [
210
+ .fadeIn(),
211
+ .slideY(from: 0.08),
212
+ ],
213
+ child: Column(
214
+ crossAxisAlignment: CrossAxisAlignment.stretch,
215
children: [
283
- Icon(
284
- _testSuccess ? Icons.check_circle : Icons.error,
285
- color: _testSuccess ? Colors.green : Colors.red,
216
+ const SizedBox(height: 32),
217
+ TextFormField(
218
+ controller: _nameController,
219
+ decoration: InputDecoration(
220
+ labelText: l10n.server_name,
221
+ filled: true,
222
+ fillColor: theme.colorScheme.surface,
223
+ ),
224
+ validator: (val) => val == null || val.trim().isEmpty
225
+ ? l10n.name_required
226
+ : null,
227
),
287
- const SizedBox(width: 8),
288
- Expanded(
289
- child: Text(
290
- _testResult!,
291
- style: TextStyle(
292
- color: _testSuccess ? Colors.green : Colors.red,
228
+ const SizedBox(height: 16),
229
+ if (!isCloud) ...[
230
+ TextFormField(
231
+ controller: _hostController,
232
+ decoration: InputDecoration(
233
+ labelText: l10n.host_label,
234
+ filled: true,
235
+ fillColor: theme.colorScheme.surface,
236
),
237
+ validator: (val) => val == null || val.trim().isEmpty
238
+ ? l10n.host_required
239
+ : null,
240
),
241
+ const SizedBox(height: 16),
242
+ TextFormField(
243
+ controller: _portController,
244
+ decoration: InputDecoration(
245
+ labelText: l10n.port_label,
246
+ filled: true,
247
+ fillColor: theme.colorScheme.surface,
248
+ ),
249
+ validator: (val) {
250
+ if (val == null || val.trim().isEmpty) {
251
+ return l10n.port_required;
252
+ } else {
253
+ if (int.tryParse(val) == null) {
254
+ return l10n.port_invalid;
255
+ } else {
256
+ return null;
257
+ }
258
+ }
259
+ },
260
+ ),
261
+ const SizedBox(height: 16),
262
+ ],
263
+ TextFormField(
264
+ controller: _apiKeyController,
265
+ decoration: InputDecoration(
266
+ labelText: isCloud
267
+ ? l10n.api_key_required
268
+ : l10n.api_key_optional,
269
+ filled: true,
270
+ fillColor: theme.colorScheme.surface,
271
+ ),
272
+ validator: (val) {
273
+ if (isCloud && (val == null || val.trim().isEmpty)) {
274
+ return l10n.api_key_required_openrouter;
275
+ }
276
+ return null;
277
+ },
278
+ obscureText: true,
279
),
280
],
281
),
282
),
299
-
300
- ShadButton.outline(
301
- width: double.infinity,
302
- onPressed: _isTesting ? null : _testConnection,
303
- child: _isTesting
304
- ? const SizedBox(
305
- width: 20,
306
- height: 20,
307
- child: CircularProgressIndicator(strokeWidth: 2),
308
- )
309
- : Text(l10n.test_connection),
310
- ),
311
- const SizedBox(height: 16),
312
-
313
- ShadButton(
314
- width: double.infinity,
315
- onPressed: _isSaving ? null : _saveAndContinue,
316
- child: _isSaving
317
- ? const SizedBox(
318
- width: 20,
319
- height: 20,
320
- child: CircularProgressIndicator(
321
- strokeWidth: 2,
322
- color: Colors.white,
323
- ),
324
- )
325
- : Text(
326
- l10n.save_continue,
327
- style: const TextStyle(
328
- fontSize: 16,
329
- fontWeight: FontWeight.w600,
283
+ Actor(
284
+ delay: 120.ms,
285
+ acts: [
286
+ .fadeIn(),
287
+ .slideY(from: 0.08),
288
+ ],
289
+ child: Column(
290
+ crossAxisAlignment: CrossAxisAlignment.stretch,
291
+ children: [
292
+ const SizedBox(height: 24),
293
+ if (_testResult != null)
294
+ Container(
295
+ padding: const EdgeInsets.all(12),
296
+ margin: const EdgeInsets.only(bottom: 24),
297
+ decoration: BoxDecoration(
298
+ color: _testSuccess
299
+ ? Colors.green.withValues(alpha: 0.1)
300
+ : Colors.red.withValues(alpha: 0.1),
301
+ borderRadius: BorderRadius.circular(8),
302
+ border: Border.all(
303
+ color: _testSuccess ? Colors.green : Colors.red,
304
+ ),
305
+ ),
306
+ child: Row(
307
+ children: [
308
+ Icon(
309
+ _testSuccess ? Icons.check_circle : Icons.error,
310
+ color: _testSuccess ? Colors.green : Colors.red,
311
+ ),
312
+ const SizedBox(width: 8),
313
+ Expanded(
314
+ child: Text(
315
+ _testResult!,
316
+ style: TextStyle(
317
+ color: _testSuccess ? Colors.green : Colors.red,
318
+ ),
319
+ ),
320
+ ),
321
+ ],
322
+ ),
323
),
324
+ ShadButton.outline(
325
+ width: double.infinity,
326
+ onPressed: _isTesting ? null : _testConnection,
327
+ child: _isTesting
328
+ ? const SizedBox(
329
+ width: 20,
330
+ height: 20,
331
+ child: CircularProgressIndicator(strokeWidth: 2),
332
+ )
333
+ : Text(l10n.test_connection),
334
),
332
- ),
333
- ],
335
+ const SizedBox(height: 16),
336
+ ShadButton(
337
+ width: double.infinity,
338
+ onPressed: _isSaving ? null : _saveAndContinue,
339
+ child: _isSaving
340
+ ? const SizedBox(
341
+ width: 20,
342
+ height: 20,
343
+ child: CircularProgressIndicator(
344
+ strokeWidth: 2,
345
+ color: Colors.white,
346
+ ),
347
+ )
348
+ : Text(
349
+ l10n.save_continue,
350
+ style: const TextStyle(
351
+ fontSize: 16,
352
+ fontWeight: FontWeight.w600,
353
+ ),
354
+ ),
355
+ ),
356
+ ],
357
+ ),
358
+ ),
359
+ ],
360
+ ),
361
),
362
),
363
),
lib/features/onboarding/screens/onboarding_server_type_screen.dart
+172
-138
@@ -3,6 +3,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
3
import 'package:go_router/go_router.dart';
4
import 'package:hugeicons/hugeicons.dart';
5
import 'package:shadcn_ui/shadcn_ui.dart';
6
+import 'package:cue/cue.dart';
7
8
import '../../../core/models/enums.dart';
9
import '../../../core/providers/app_providers.dart';
@@ -60,156 +61,189 @@ class _OnboardingServerTypeScreenState
61
),
62
],
63
),
63
- body: Column(
64
- crossAxisAlignment: CrossAxisAlignment.stretch,
65
- children: [
66
- Expanded(
67
- child: SingleChildScrollView(
68
- padding: const EdgeInsets.symmetric(horizontal: 24.0),
69
- child: Column(
70
- crossAxisAlignment: CrossAxisAlignment.stretch,
71
- children: [
72
- const SizedBox(height: 24),
73
- Text(
74
- l10n.onboarding_connect_server,
75
- textAlign: TextAlign.center,
76
- style: theme.textTheme.headlineLarge?.copyWith(
77
- fontWeight: FontWeight.w800,
78
- height: 1.1,
79
- ),
80
- ),
81
- const SizedBox(height: 16),
82
- Text(
83
- l10n.onboarding_connect_desc,
84
- textAlign: TextAlign.center,
85
- style: theme.textTheme.bodySmall?.copyWith(
86
- color: theme.colorScheme.onSurface.withValues(alpha: 0.7),
87
- height: 1.5,
88
- ),
89
- ),
90
- const SizedBox(height: 24),
91
- _buildServerCard(
92
- type: ServerType.onDevice,
93
- title: l10n.server_type_on_device,
94
- subtitle: l10n.server_type_on_device_sub,
95
- iconWidget: Icon(
96
- Icons.phone_android_rounded,
97
- color: _selectedType == ServerType.onDevice
98
- ? theme.colorScheme.primary
99
- : theme.colorScheme.onSurface,
100
- ),
101
- theme: theme,
102
- ),
103
- const SizedBox(height: 16),
104
- _buildServerCard(
105
- type: ServerType.lmStudio,
106
- title: l10n.server_type_lm_studio,
107
- subtitle: l10n.server_type_lm_studio_sub,
108
- iconWidget: Icon(
109
- Icons.terminal_rounded,
110
- color: _selectedType == ServerType.lmStudio
111
- ? theme.colorScheme.primary
112
- : theme.colorScheme.onSurface,
113
- ),
114
- theme: theme,
115
- ),
116
- const SizedBox(height: 16),
117
- _buildServerCard(
118
- type: ServerType.ollama,
119
- title: l10n.server_type_ollama,
120
- subtitle: l10n.server_type_ollama_sub,
121
- iconWidget: Icon(
122
- Icons.smart_toy_rounded,
123
- color: _selectedType == ServerType.ollama
124
- ? theme.colorScheme.primary
125
- : theme.colorScheme.onSurface,
64
+ body: Cue.onMount(
65
+ motion: .smooth(),
66
+ child: Column(
67
+ crossAxisAlignment: CrossAxisAlignment.stretch,
68
+ children: [
69
+ Expanded(
70
+ child: SingleChildScrollView(
71
+ padding: const EdgeInsets.symmetric(horizontal: 24.0),
72
+ child: Column(
73
+ crossAxisAlignment: CrossAxisAlignment.stretch,
74
+ children: [
75
+ Actor(
76
+ acts: [
77
+ .fadeIn(),
78
+ .slideY(from: 0.08),
79
+ ],
80
+ child: Column(
81
+ crossAxisAlignment: CrossAxisAlignment.stretch,
82
+ children: [
83
+ const SizedBox(height: 24),
84
+ Text(
85
+ l10n.onboarding_connect_server,
86
+ textAlign: TextAlign.center,
87
+ style: theme.textTheme.headlineLarge?.copyWith(
88
+ fontWeight: FontWeight.w800,
89
+ height: 1.1,
90
+ ),
91
+ ),
92
+ const SizedBox(height: 16),
93
+ Text(
94
+ l10n.onboarding_connect_desc,
95
+ textAlign: TextAlign.center,
96
+ style: theme.textTheme.bodySmall?.copyWith(
97
+ color: theme.colorScheme.onSurface.withValues(alpha: 0.7),
98
+ height: 1.5,
99
+ ),
100
+ ),
101
+ ],
102
+ ),
103
),
127
- theme: theme,
128
- ),
129
- const SizedBox(height: 16),
130
- _buildServerCard(
131
- type: ServerType.openRouter,
132
- title: l10n.server_type_openrouter,
133
- subtitle: l10n.server_type_openrouter_sub,
134
- iconWidget: HugeIcon(
135
- icon: HugeIcons.strokeRoundedCloudServer,
136
- color: _selectedType == ServerType.openRouter
137
- ? theme.colorScheme.primary
138
- : theme.colorScheme.onSurface,
104
+ Actor(
105
+ delay: 60.ms,
106
+ acts: [
107
+ .fadeIn(),
108
+ .slideY(from: 0.08),
109
+ ],
110
+ child: Column(
111
+ crossAxisAlignment: CrossAxisAlignment.stretch,
112
+ children: [
113
+ const SizedBox(height: 24),
114
+ _buildServerCard(
115
+ type: ServerType.onDevice,
116
+ title: l10n.server_type_on_device,
117
+ subtitle: l10n.server_type_on_device_sub,
118
+ iconWidget: Icon(
119
+ Icons.phone_android_rounded,
120
+ color: _selectedType == ServerType.onDevice
121
+ ? theme.colorScheme.primary
122
+ : theme.colorScheme.onSurface,
123
+ ),
124
+ theme: theme,
125
+ ),
126
+ const SizedBox(height: 16),
127
+ _buildServerCard(
128
+ type: ServerType.lmStudio,
129
+ title: l10n.server_type_lm_studio,
130
+ subtitle: l10n.server_type_lm_studio_sub,
131
+ iconWidget: Icon(
132
+ Icons.terminal_rounded,
133
+ color: _selectedType == ServerType.lmStudio
134
+ ? theme.colorScheme.primary
135
+ : theme.colorScheme.onSurface,
136
+ ),
137
+ theme: theme,
138
+ ),
139
+ const SizedBox(height: 16),
140
+ _buildServerCard(
141
+ type: ServerType.ollama,
142
+ title: l10n.server_type_ollama,
143
+ subtitle: l10n.server_type_ollama_sub,
144
+ iconWidget: Icon(
145
+ Icons.smart_toy_rounded,
146
+ color: _selectedType == ServerType.ollama
147
+ ? theme.colorScheme.primary
148
+ : theme.colorScheme.onSurface,
149
+ ),
150
+ theme: theme,
151
+ ),
152
+ const SizedBox(height: 16),
153
+ _buildServerCard(
154
+ type: ServerType.openRouter,
155
+ title: l10n.server_type_openrouter,
156
+ subtitle: l10n.server_type_openrouter_sub,
157
+ iconWidget: HugeIcon(
158
+ icon: HugeIcons.strokeRoundedCloudServer,
159
+ color: _selectedType == ServerType.openRouter
160
+ ? theme.colorScheme.primary
161
+ : theme.colorScheme.onSurface,
162
+ ),
163
+ theme: theme,
164
+ ),
165
+ ],
166
+ ),
167
),
140
- theme: theme,
141
- ),
142
- const SizedBox(height: 32),
143
- ],
168
+ const SizedBox(height: 32),
169
+ ],
170
+ ),
171
),
172
),
146
- ),
147
- Padding(
148
- padding: const EdgeInsets.symmetric(horizontal: 24.0),
149
- child: Column(
150
- children: [
151
- // Status Indicator
152
- Row(
153
- mainAxisAlignment: MainAxisAlignment.center,
173
+ Actor(
174
+ delay: 120.ms,
175
+ acts: [
176
+ .fadeIn(),
177
+ .slideY(from: 0.08),
178
+ ],
179
+ child: Padding(
180
+ padding: const EdgeInsets.symmetric(horizontal: 24.0),
181
+ child: Column(
182
children: [
155
- Container(
156
- width: 8,
157
- height: 8,
158
- decoration: BoxDecoration(
159
- color: _selectedType != null
160
- ? theme.colorScheme.primary
161
- : theme.colorScheme.onSurface.withValues(
162
- alpha: 0.4,
163
- ),
164
- shape: BoxShape.circle,
165
- ),
183
+ // Status Indicator
184
+ Row(
185
+ mainAxisAlignment: MainAxisAlignment.center,
186
+ children: [
187
+ Container(
188
+ width: 8,
189
+ height: 8,
190
+ decoration: BoxDecoration(
191
+ color: _selectedType != null
192
+ ? theme.colorScheme.primary
193
+ : theme.colorScheme.onSurface.withValues(
194
+ alpha: 0.4,
195
+ ),
196
+ shape: BoxShape.circle,
197
+ ),
198
+ ),
199
+ const SizedBox(width: 8),
200
+ Text(
201
+ _selectedType != null
202
+ ? l10n.ready_continue
203
+ : l10n.waiting_selection,
204
+ style: theme.textTheme.labelSmall?.copyWith(
205
+ color: theme.colorScheme.onSurface.withValues(
206
+ alpha: 0.5,
207
+ ),
208
+ letterSpacing: 2,
209
+ fontWeight: FontWeight.w600,
210
+ ),
211
+ ),
212
+ ],
213
),
167
- const SizedBox(width: 8),
168
- Text(
169
- _selectedType != null
170
- ? l10n.ready_continue
171
- : l10n.waiting_selection,
172
- style: theme.textTheme.labelSmall?.copyWith(
173
- color: theme.colorScheme.onSurface.withValues(
174
- alpha: 0.5,
214
+ const SizedBox(height: 16),
215
+ ShadButton(
216
+ width: double.infinity,
217
+ enabled: _selectedType != null,
218
+ onPressed: () {
219
+ if (_selectedType != null) {
220
+ if (_selectedType == ServerType.onDevice) {
221
+ context.push(AppRoutes.onboardingModelDownload);
222
+ } else {
223
+ context.push(
224
+ AppRoutes.onboardingSetup,
225
+ extra: _selectedType,
226
+ );
227
+ }
228
+ }
229
+ },
230
+ child: Text(
231
+ l10n.continue_action,
232
+ style: const TextStyle(
233
+ fontSize: 16,
234
+ fontWeight: FontWeight.w600,
235
),
176
- letterSpacing: 2,
177
- fontWeight: FontWeight.w600,
236
),
237
),
238
+ SizedBox(
239
+ height: MediaQuery.of(context).padding.bottom,
240
+ ), // Reduced bottom padding slightly for small screens
241
],
242
),
182
- const SizedBox(height: 16),
183
- ShadButton(
184
- width: double.infinity,
185
- enabled: _selectedType != null,
186
- onPressed: () {
187
- if (_selectedType != null) {
188
- if (_selectedType == ServerType.onDevice) {
189
- context.push(AppRoutes.onboardingModelDownload);
190
- } else {
191
- context.push(
192
- AppRoutes.onboardingSetup,
193
- extra: _selectedType,
194
- );
195
- }
196
- }
197
- },
198
- child: Text(
199
- l10n.continue_action,
200
- style: const TextStyle(
201
- fontSize: 16,
202
- fontWeight: FontWeight.w600,
203
- ),
204
- ),
205
- ),
206
- SizedBox(
207
- height: MediaQuery.of(context).padding.bottom,
208
- ), // Reduced bottom padding slightly for small screens
209
- ],
243
+ ),
244
),
211
- ),
212
- ],
245
+ ],
246
+ ),
247
),
248
);
249
}
lib/features/onboarding/screens/onboarding_theme_screen.dart
+107
-83
@@ -3,6 +3,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
3
import 'package:go_router/go_router.dart';
4
import 'package:hugeicons/hugeicons.dart';
5
import 'package:shadcn_ui/shadcn_ui.dart';
6
+import 'package:cue/cue.dart';
7
8
import '../../../core/providers/app_providers.dart';
9
import '../../../core/routes/app_routes.dart';
@@ -35,95 +36,118 @@ class _OnboardingThemeScreenState extends ConsumerState<OnboardingThemeScreen> {
36
),
37
),
38
body: SafeArea(
38
- child: Padding(
39
- padding: const EdgeInsets.symmetric(horizontal: 24.0, vertical: 16.0),
40
- child: Column(
41
- crossAxisAlignment: CrossAxisAlignment.stretch,
42
- children: [
43
- Text(
44
- l10n.choose_theme_desc,
45
- style: theme.textTheme.titleMedium?.copyWith(
46
- color: theme.colorScheme.onSurface.withValues(alpha: 0.7),
39
+ child: Cue.onMount(
40
+ motion: .smooth(),
41
+ child: Padding(
42
+ padding: const EdgeInsets.symmetric(horizontal: 24.0, vertical: 16.0),
43
+ child: Column(
44
+ crossAxisAlignment: CrossAxisAlignment.stretch,
45
+ children: [
46
+ Actor(
47
+ acts: [
48
+ .fadeIn(),
49
+ .slideY(from: 0.08),
50
+ ],
51
+ child: Text(
52
+ l10n.choose_theme_desc,
53
+ style: theme.textTheme.titleMedium?.copyWith(
54
+ color: theme.colorScheme.onSurface.withValues(alpha: 0.7),
55
+ ),
56
+ ),
57
),
48
- ),
49
- const SizedBox(height: 32),
58
+ const SizedBox(height: 32),
59
51
- Expanded(
52
- child: ListView(
53
- children: [
54
- _buildThemeCard(
55
- type: AppThemeType.system,
56
- title: l10n.theme_card_system,
57
- subtitle: l10n.theme_card_system_sub,
58
- iconWidget: HugeIcon(
59
- icon: HugeIcons.strokeRoundedSettings01,
60
- color: currentTheme == AppThemeType.system
61
- ? theme.colorScheme.primary
62
- : theme.colorScheme.onSurface,
63
- ),
64
- currentTheme: currentTheme,
65
- context: context,
66
- ),
67
- const SizedBox(height: 16),
68
- _buildThemeCard(
69
- type: AppThemeType.light,
70
- title: l10n.theme_card_light,
71
- subtitle: l10n.theme_card_light_sub,
72
- iconWidget: HugeIcon(
73
- icon: HugeIcons.strokeRoundedSun01,
74
- color: currentTheme == AppThemeType.light
75
- ? theme.colorScheme.primary
76
- : theme.colorScheme.onSurface,
77
- ),
78
- currentTheme: currentTheme,
79
- context: context,
80
- ),
81
- const SizedBox(height: 16),
82
- _buildThemeCard(
83
- type: AppThemeType.dark,
84
- title: l10n.theme_card_dark,
85
- subtitle: l10n.theme_card_dark_sub,
86
- iconWidget: HugeIcon(
87
- icon: HugeIcons.strokeRoundedMoon02,
88
- color: currentTheme == AppThemeType.dark
89
- ? theme.colorScheme.primary
90
- : theme.colorScheme.onSurface,
91
- ),
92
- currentTheme: currentTheme,
93
- context: context,
94
- ),
95
- const SizedBox(height: 16),
96
- _buildThemeCard(
97
- type: AppThemeType.claude,
98
- title: l10n.theme_card_claude,
99
- subtitle: l10n.theme_card_claude_sub,
100
- iconWidget: HugeIcon(
101
- icon: HugeIcons.strokeRoundedPaintBrush02,
102
- color: currentTheme == AppThemeType.claude
103
- ? theme.colorScheme.primary
104
- : theme.colorScheme.onSurface,
105
- ),
106
- currentTheme: currentTheme,
107
- context: context,
60
+ Expanded(
61
+ child: Actor(
62
+ delay: 60.ms,
63
+ acts: [
64
+ .fadeIn(),
65
+ .slideY(from: 0.08),
66
+ ],
67
+ child: ListView(
68
+ children: [
69
+ _buildThemeCard(
70
+ type: AppThemeType.system,
71
+ title: l10n.theme_card_system,
72
+ subtitle: l10n.theme_card_system_sub,
73
+ iconWidget: HugeIcon(
74
+ icon: HugeIcons.strokeRoundedSettings01,
75
+ color: currentTheme == AppThemeType.system
76
+ ? theme.colorScheme.primary
77
+ : theme.colorScheme.onSurface,
78
+ ),
79
+ currentTheme: currentTheme,
80
+ context: context,
81
+ ),
82
+ const SizedBox(height: 16),
83
+ _buildThemeCard(
84
+ type: AppThemeType.light,
85
+ title: l10n.theme_card_light,
86
+ subtitle: l10n.theme_card_light_sub,
87
+ iconWidget: HugeIcon(
88
+ icon: HugeIcons.strokeRoundedSun01,
89
+ color: currentTheme == AppThemeType.light
90
+ ? theme.colorScheme.primary
91
+ : theme.colorScheme.onSurface,
92
+ ),
93
+ currentTheme: currentTheme,
94
+ context: context,
95
+ ),
96
+ const SizedBox(height: 16),
97
+ _buildThemeCard(
98
+ type: AppThemeType.dark,
99
+ title: l10n.theme_card_dark,
100
+ subtitle: l10n.theme_card_dark_sub,
101
+ iconWidget: HugeIcon(
102
+ icon: HugeIcons.strokeRoundedMoon02,
103
+ color: currentTheme == AppThemeType.dark
104
+ ? theme.colorScheme.primary
105
+ : theme.colorScheme.onSurface,
106
+ ),
107
+ currentTheme: currentTheme,
108
+ context: context,
109
+ ),
110
+ const SizedBox(height: 16),
111
+ _buildThemeCard(
112
+ type: AppThemeType.claude,
113
+ title: l10n.theme_card_claude,
114
+ subtitle: l10n.theme_card_claude_sub,
115
+ iconWidget: HugeIcon(
116
+ icon: HugeIcons.strokeRoundedPaintBrush02,
117
+ color: currentTheme == AppThemeType.claude
118
+ ? theme.colorScheme.primary
119
+ : theme.colorScheme.onSurface,
120
+ ),
121
+ currentTheme: currentTheme,
122
+ context: context,
123
+ ),
124
+ ],
125
),
109
- ],
126
+ ),
127
),
111
- ),
128
113
- const SizedBox(height: 24),
114
- ShadButton(
115
- width: double.infinity,
116
- onPressed: () async {
117
- if (context.mounted) {
118
- context.push(AppRoutes.onboardingNotifications);
119
- }
120
- },
121
- child: Text(
122
- l10n.finish_setup,
123
- style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w600),
129
+ const SizedBox(height: 24),
130
+ Actor(
131
+ delay: 120.ms,
132
+ acts: [
133
+ .fadeIn(),
134
+ .slideY(from: 0.08),
135
+ ],
136
+ child: ShadButton(
137
+ width: double.infinity,
138
+ onPressed: () async {
139
+ if (context.mounted) {
140
+ context.push(AppRoutes.onboardingNotifications);
141
+ }
142
+ },
143
+ child: Text(
144
+ l10n.finish_setup,
145
+ style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w600),
146
+ ),
147
+ ),
148
),
125
- ),
126
- ],
149
+ ],
150
+ ),
151
),
152
),
153
),
pubspec.lock
+16
-8
@@ -273,6 +273,14 @@ packages:
273
url: "https://pub.dev"
274
source: hosted
275
version: "3.0.7"
276
+ cue:
277
+ dependency: "direct main"
278
+ description:
279
+ name: cue
280
+ sha256: c98b4a3fffcc1726113d0b4d94d70fa2f9c8061382bddb2e456726392053eb4c
281
+ url: "https://pub.dev"
282
+ source: hosted
283
+ version: "0.3.1"
284
cupertino_icons:
285
dependency: "direct main"
286
description:
@@ -801,10 +809,10 @@ packages:
809
dependency: transitive
810
description:
811
name: matcher
804
- sha256: "12956d0ad8390bbcc63ca2e1469c0619946ccb52809807067a7020d57e647aa6"
812
+ sha256: dc0b7dc7651697ea4ff3e69ef44b0407ea32c487a39fff6a4004fa585e901861
813
url: "https://pub.dev"
814
source: hosted
807
- version: "0.12.18"
815
+ version: "0.12.19"
816
material_color_utilities:
817
dependency: transitive
818
description:
@@ -1367,26 +1375,26 @@ packages:
1375
dependency: transitive
1376
description:
1377
name: test
1370
- sha256: "54c516bbb7cee2754d327ad4fca637f78abfc3cbcc5ace83b3eda117e42cd71a"
1378
+ sha256: "280d6d890011ca966ad08df7e8a4ddfab0fb3aa49f96ed6de56e3521347a9ae7"
1379
url: "https://pub.dev"
1380
source: hosted
1373
- version: "1.29.0"
1381
+ version: "1.30.0"
1382
test_api:
1383
dependency: transitive
1384
description:
1385
name: test_api
1378
- sha256: "93167629bfc610f71560ab9312acdda4959de4df6fac7492c89ff0d3886f6636"
1386
+ sha256: "8161c84903fd860b26bfdefb7963b3f0b68fee7adea0f59ef805ecca346f0c7a"
1387
url: "https://pub.dev"
1388
source: hosted
1381
- version: "0.7.9"
1389
+ version: "0.7.10"
1390
test_core:
1391
dependency: transitive
1392
description:
1393
name: test_core
1386
- sha256: "394f07d21f0f2255ec9e3989f21e54d3c7dc0e6e9dbce160e5a9c1a6be0e2943"
1394
+ sha256: "0381bd1585d1a924763c308100f2138205252fb90c9d4eeaf28489ee65ccde51"
1395
url: "https://pub.dev"
1396
source: hosted
1389
- version: "0.6.15"
1397
+ version: "0.6.16"
1398
theme_extensions_builder_annotation:
1399
dependency: transitive
1400
description:
pubspec.yaml
+2
-1
@@ -16,7 +16,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev
16
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
17
# In Windows, build-name is used as the major, minor, and patch parts
18
# of the product and file versions while build-number is used as the build suffix.
19
-version: 1.3.4+36
19
+version: 1.3.6+38
20
21
environment:
22
sdk: ^3.11.0
@@ -53,6 +53,7 @@ dependencies:
53
git:
54
url: https://github.com/abdulmominsakib/syntax_highlight.git
55
animations: 2.1.2
56
+ cue: ^0.3.1
57
logger: ^2.7.0
58
shared_preferences: ^2.5.5
59
objectbox: ^5.3.1