tools-template-cli 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (198) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +54 -0
  3. package/dist/index.js +380 -0
  4. package/dist/templates.js +44 -0
  5. package/package.json +49 -0
  6. package/supabase-expo-app/.env.example +2 -0
  7. package/supabase-expo-app/App.tsx +55 -0
  8. package/supabase-expo-app/README.md +179 -0
  9. package/supabase-expo-app/app.json +34 -0
  10. package/supabase-expo-app/assets/adaptive-icon.png +0 -0
  11. package/supabase-expo-app/assets/favicon.png +0 -0
  12. package/supabase-expo-app/assets/icon.png +0 -0
  13. package/supabase-expo-app/assets/splash-icon.png +0 -0
  14. package/supabase-expo-app/components/Account.tsx +234 -0
  15. package/supabase-expo-app/components/Auth.tsx +161 -0
  16. package/supabase-expo-app/components/Avatar.tsx +173 -0
  17. package/supabase-expo-app/index.ts +8 -0
  18. package/supabase-expo-app/lib/supabase.ts +63 -0
  19. package/supabase-expo-app/package-lock.json +8983 -0
  20. package/supabase-expo-app/package.json +32 -0
  21. package/supabase-expo-app/scripts/setup-supabase.sh +73 -0
  22. package/supabase-expo-app/supabase/schema.sql +57 -0
  23. package/supabase-expo-app/tsconfig.json +6 -0
  24. package/supabase-swiftui-app/Package.swift +32 -0
  25. package/supabase-swiftui-app/README.md +215 -0
  26. package/supabase-swiftui-app/SupabaseSwiftUIApp/AccountView.swift +181 -0
  27. package/supabase-swiftui-app/SupabaseSwiftUIApp/AuthView.swift +123 -0
  28. package/supabase-swiftui-app/SupabaseSwiftUIApp/AvatarView.swift +113 -0
  29. package/supabase-swiftui-app/SupabaseSwiftUIApp/ContentView.swift +18 -0
  30. package/supabase-swiftui-app/SupabaseSwiftUIApp/Supabase.swift +13 -0
  31. package/supabase-swiftui-app/SupabaseSwiftUIApp/SupabaseSwiftUIApp.swift +22 -0
  32. package/supabase-swiftui-app/scripts/setup-supabase.sh +67 -0
  33. package/supabase-swiftui-app/supabase/schema.sql +57 -0
  34. package/supabase-user-management/AGENTS.md +5 -0
  35. package/supabase-user-management/CLAUDE.md +1 -0
  36. package/supabase-user-management/README.md +178 -0
  37. package/supabase-user-management/app/account/account-form.tsx +174 -0
  38. package/supabase-user-management/app/account/avatar.tsx +109 -0
  39. package/supabase-user-management/app/account/page.tsx +28 -0
  40. package/supabase-user-management/app/auth/confirm/route.ts +26 -0
  41. package/supabase-user-management/app/error.tsx +14 -0
  42. package/supabase-user-management/app/favicon.ico +0 -0
  43. package/supabase-user-management/app/globals.css +130 -0
  44. package/supabase-user-management/app/layout.tsx +22 -0
  45. package/supabase-user-management/app/loading.tsx +7 -0
  46. package/supabase-user-management/app/login/actions.ts +45 -0
  47. package/supabase-user-management/app/login/page.tsx +90 -0
  48. package/supabase-user-management/app/page.tsx +16 -0
  49. package/supabase-user-management/components/ui/button.tsx +58 -0
  50. package/supabase-user-management/components.json +25 -0
  51. package/supabase-user-management/eslint.config.mjs +18 -0
  52. package/supabase-user-management/lib/supabase/client.ts +8 -0
  53. package/supabase-user-management/lib/supabase/middleware.ts +52 -0
  54. package/supabase-user-management/lib/supabase/server.ts +29 -0
  55. package/supabase-user-management/lib/utils.ts +6 -0
  56. package/supabase-user-management/next.config.ts +7 -0
  57. package/supabase-user-management/package-lock.json +9910 -0
  58. package/supabase-user-management/package.json +36 -0
  59. package/supabase-user-management/postcss.config.mjs +7 -0
  60. package/supabase-user-management/public/file.svg +1 -0
  61. package/supabase-user-management/public/globe.svg +1 -0
  62. package/supabase-user-management/public/next.svg +1 -0
  63. package/supabase-user-management/public/vercel.svg +1 -0
  64. package/supabase-user-management/public/window.svg +1 -0
  65. package/supabase-user-management/scripts/setup-supabase.sh +98 -0
  66. package/supabase-user-management/src/proxy.ts +12 -0
  67. package/supabase-user-management/supabase/schema.sql +57 -0
  68. package/supabase-user-management/tsconfig.json +34 -0
  69. package/supabase_flutter_app/.metadata +45 -0
  70. package/supabase_flutter_app/README.md +195 -0
  71. package/supabase_flutter_app/analysis_options.yaml +28 -0
  72. package/supabase_flutter_app/android/app/build.gradle.kts +44 -0
  73. package/supabase_flutter_app/android/app/src/debug/AndroidManifest.xml +7 -0
  74. package/supabase_flutter_app/android/app/src/main/AndroidManifest.xml +54 -0
  75. package/supabase_flutter_app/android/app/src/main/kotlin/com/example/supabase_flutter_app/MainActivity.kt +5 -0
  76. package/supabase_flutter_app/android/app/src/main/res/drawable/launch_background.xml +12 -0
  77. package/supabase_flutter_app/android/app/src/main/res/drawable-v21/launch_background.xml +12 -0
  78. package/supabase_flutter_app/android/app/src/main/res/mipmap-hdpi/ic_launcher.png +0 -0
  79. package/supabase_flutter_app/android/app/src/main/res/mipmap-mdpi/ic_launcher.png +0 -0
  80. package/supabase_flutter_app/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png +0 -0
  81. package/supabase_flutter_app/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png +0 -0
  82. package/supabase_flutter_app/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png +0 -0
  83. package/supabase_flutter_app/android/app/src/main/res/values/styles.xml +18 -0
  84. package/supabase_flutter_app/android/app/src/main/res/values-night/styles.xml +18 -0
  85. package/supabase_flutter_app/android/app/src/profile/AndroidManifest.xml +7 -0
  86. package/supabase_flutter_app/android/build.gradle.kts +24 -0
  87. package/supabase_flutter_app/android/gradle/wrapper/gradle-wrapper.properties +5 -0
  88. package/supabase_flutter_app/android/gradle.properties +2 -0
  89. package/supabase_flutter_app/android/settings.gradle.kts +26 -0
  90. package/supabase_flutter_app/ios/Flutter/AppFrameworkInfo.plist +26 -0
  91. package/supabase_flutter_app/ios/Flutter/Debug.xcconfig +2 -0
  92. package/supabase_flutter_app/ios/Flutter/Release.xcconfig +2 -0
  93. package/supabase_flutter_app/ios/Podfile +43 -0
  94. package/supabase_flutter_app/ios/Runner/AppDelegate.swift +13 -0
  95. package/supabase_flutter_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json +122 -0
  96. package/supabase_flutter_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png +0 -0
  97. package/supabase_flutter_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png +0 -0
  98. package/supabase_flutter_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png +0 -0
  99. package/supabase_flutter_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png +0 -0
  100. package/supabase_flutter_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png +0 -0
  101. package/supabase_flutter_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png +0 -0
  102. package/supabase_flutter_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png +0 -0
  103. package/supabase_flutter_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png +0 -0
  104. package/supabase_flutter_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png +0 -0
  105. package/supabase_flutter_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png +0 -0
  106. package/supabase_flutter_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png +0 -0
  107. package/supabase_flutter_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png +0 -0
  108. package/supabase_flutter_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png +0 -0
  109. package/supabase_flutter_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png +0 -0
  110. package/supabase_flutter_app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png +0 -0
  111. package/supabase_flutter_app/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json +23 -0
  112. package/supabase_flutter_app/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png +0 -0
  113. package/supabase_flutter_app/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png +0 -0
  114. package/supabase_flutter_app/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png +0 -0
  115. package/supabase_flutter_app/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md +5 -0
  116. package/supabase_flutter_app/ios/Runner/Base.lproj/LaunchScreen.storyboard +37 -0
  117. package/supabase_flutter_app/ios/Runner/Base.lproj/Main.storyboard +26 -0
  118. package/supabase_flutter_app/ios/Runner/Info.plist +61 -0
  119. package/supabase_flutter_app/ios/Runner/Runner-Bridging-Header.h +1 -0
  120. package/supabase_flutter_app/ios/Runner.xcodeproj/project.pbxproj +619 -0
  121. package/supabase_flutter_app/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -0
  122. package/supabase_flutter_app/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
  123. package/supabase_flutter_app/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings +8 -0
  124. package/supabase_flutter_app/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +101 -0
  125. package/supabase_flutter_app/ios/Runner.xcworkspace/contents.xcworkspacedata +7 -0
  126. package/supabase_flutter_app/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
  127. package/supabase_flutter_app/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings +8 -0
  128. package/supabase_flutter_app/ios/RunnerTests/RunnerTests.swift +12 -0
  129. package/supabase_flutter_app/lib/components/avatar.dart +153 -0
  130. package/supabase_flutter_app/lib/main.dart +70 -0
  131. package/supabase_flutter_app/lib/pages/account_page.dart +189 -0
  132. package/supabase_flutter_app/lib/pages/login_page.dart +150 -0
  133. package/supabase_flutter_app/linux/CMakeLists.txt +128 -0
  134. package/supabase_flutter_app/linux/flutter/CMakeLists.txt +88 -0
  135. package/supabase_flutter_app/linux/flutter/generated_plugin_registrant.cc +23 -0
  136. package/supabase_flutter_app/linux/flutter/generated_plugin_registrant.h +15 -0
  137. package/supabase_flutter_app/linux/flutter/generated_plugins.cmake +26 -0
  138. package/supabase_flutter_app/linux/runner/CMakeLists.txt +26 -0
  139. package/supabase_flutter_app/linux/runner/main.cc +6 -0
  140. package/supabase_flutter_app/linux/runner/my_application.cc +148 -0
  141. package/supabase_flutter_app/linux/runner/my_application.h +21 -0
  142. package/supabase_flutter_app/macos/Flutter/Flutter-Debug.xcconfig +2 -0
  143. package/supabase_flutter_app/macos/Flutter/Flutter-Release.xcconfig +2 -0
  144. package/supabase_flutter_app/macos/Flutter/GeneratedPluginRegistrant.swift +18 -0
  145. package/supabase_flutter_app/macos/Podfile +42 -0
  146. package/supabase_flutter_app/macos/Runner/AppDelegate.swift +13 -0
  147. package/supabase_flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json +68 -0
  148. package/supabase_flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png +0 -0
  149. package/supabase_flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png +0 -0
  150. package/supabase_flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png +0 -0
  151. package/supabase_flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png +0 -0
  152. package/supabase_flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png +0 -0
  153. package/supabase_flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png +0 -0
  154. package/supabase_flutter_app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png +0 -0
  155. package/supabase_flutter_app/macos/Runner/Base.lproj/MainMenu.xib +343 -0
  156. package/supabase_flutter_app/macos/Runner/Configs/AppInfo.xcconfig +14 -0
  157. package/supabase_flutter_app/macos/Runner/Configs/Debug.xcconfig +2 -0
  158. package/supabase_flutter_app/macos/Runner/Configs/Release.xcconfig +2 -0
  159. package/supabase_flutter_app/macos/Runner/Configs/Warnings.xcconfig +13 -0
  160. package/supabase_flutter_app/macos/Runner/DebugProfile.entitlements +12 -0
  161. package/supabase_flutter_app/macos/Runner/Info.plist +32 -0
  162. package/supabase_flutter_app/macos/Runner/MainFlutterWindow.swift +15 -0
  163. package/supabase_flutter_app/macos/Runner/Release.entitlements +8 -0
  164. package/supabase_flutter_app/macos/Runner.xcodeproj/project.pbxproj +705 -0
  165. package/supabase_flutter_app/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
  166. package/supabase_flutter_app/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +99 -0
  167. package/supabase_flutter_app/macos/Runner.xcworkspace/contents.xcworkspacedata +7 -0
  168. package/supabase_flutter_app/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
  169. package/supabase_flutter_app/macos/RunnerTests/RunnerTests.swift +12 -0
  170. package/supabase_flutter_app/pubspec.lock +818 -0
  171. package/supabase_flutter_app/pubspec.yaml +26 -0
  172. package/supabase_flutter_app/scripts/setup-supabase.sh +72 -0
  173. package/supabase_flutter_app/supabase/schema.sql +57 -0
  174. package/supabase_flutter_app/test/widget_test.dart +30 -0
  175. package/supabase_flutter_app/web/favicon.png +0 -0
  176. package/supabase_flutter_app/web/icons/Icon-192.png +0 -0
  177. package/supabase_flutter_app/web/icons/Icon-512.png +0 -0
  178. package/supabase_flutter_app/web/icons/Icon-maskable-192.png +0 -0
  179. package/supabase_flutter_app/web/icons/Icon-maskable-512.png +0 -0
  180. package/supabase_flutter_app/web/index.html +38 -0
  181. package/supabase_flutter_app/web/manifest.json +35 -0
  182. package/supabase_flutter_app/windows/CMakeLists.txt +108 -0
  183. package/supabase_flutter_app/windows/flutter/CMakeLists.txt +109 -0
  184. package/supabase_flutter_app/windows/flutter/generated_plugin_registrant.cc +20 -0
  185. package/supabase_flutter_app/windows/flutter/generated_plugin_registrant.h +15 -0
  186. package/supabase_flutter_app/windows/flutter/generated_plugins.cmake +26 -0
  187. package/supabase_flutter_app/windows/runner/CMakeLists.txt +40 -0
  188. package/supabase_flutter_app/windows/runner/Runner.rc +121 -0
  189. package/supabase_flutter_app/windows/runner/flutter_window.cpp +71 -0
  190. package/supabase_flutter_app/windows/runner/flutter_window.h +33 -0
  191. package/supabase_flutter_app/windows/runner/main.cpp +43 -0
  192. package/supabase_flutter_app/windows/runner/resource.h +16 -0
  193. package/supabase_flutter_app/windows/runner/resources/app_icon.ico +0 -0
  194. package/supabase_flutter_app/windows/runner/runner.exe.manifest +14 -0
  195. package/supabase_flutter_app/windows/runner/utils.cpp +65 -0
  196. package/supabase_flutter_app/windows/runner/utils.h +19 -0
  197. package/supabase_flutter_app/windows/runner/win32_window.cpp +288 -0
  198. package/supabase_flutter_app/windows/runner/win32_window.h +102 -0
@@ -0,0 +1,101 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <Scheme
3
+ LastUpgradeVersion = "1510"
4
+ version = "1.3">
5
+ <BuildAction
6
+ parallelizeBuildables = "YES"
7
+ buildImplicitDependencies = "YES">
8
+ <BuildActionEntries>
9
+ <BuildActionEntry
10
+ buildForTesting = "YES"
11
+ buildForRunning = "YES"
12
+ buildForProfiling = "YES"
13
+ buildForArchiving = "YES"
14
+ buildForAnalyzing = "YES">
15
+ <BuildableReference
16
+ BuildableIdentifier = "primary"
17
+ BlueprintIdentifier = "97C146ED1CF9000F007C117D"
18
+ BuildableName = "Runner.app"
19
+ BlueprintName = "Runner"
20
+ ReferencedContainer = "container:Runner.xcodeproj">
21
+ </BuildableReference>
22
+ </BuildActionEntry>
23
+ </BuildActionEntries>
24
+ </BuildAction>
25
+ <TestAction
26
+ buildConfiguration = "Debug"
27
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
28
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
29
+ customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit"
30
+ shouldUseLaunchSchemeArgsEnv = "YES">
31
+ <MacroExpansion>
32
+ <BuildableReference
33
+ BuildableIdentifier = "primary"
34
+ BlueprintIdentifier = "97C146ED1CF9000F007C117D"
35
+ BuildableName = "Runner.app"
36
+ BlueprintName = "Runner"
37
+ ReferencedContainer = "container:Runner.xcodeproj">
38
+ </BuildableReference>
39
+ </MacroExpansion>
40
+ <Testables>
41
+ <TestableReference
42
+ skipped = "NO"
43
+ parallelizable = "YES">
44
+ <BuildableReference
45
+ BuildableIdentifier = "primary"
46
+ BlueprintIdentifier = "331C8080294A63A400263BE5"
47
+ BuildableName = "RunnerTests.xctest"
48
+ BlueprintName = "RunnerTests"
49
+ ReferencedContainer = "container:Runner.xcodeproj">
50
+ </BuildableReference>
51
+ </TestableReference>
52
+ </Testables>
53
+ </TestAction>
54
+ <LaunchAction
55
+ buildConfiguration = "Debug"
56
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
57
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
58
+ customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit"
59
+ launchStyle = "0"
60
+ useCustomWorkingDirectory = "NO"
61
+ ignoresPersistentStateOnLaunch = "NO"
62
+ debugDocumentVersioning = "YES"
63
+ debugServiceExtension = "internal"
64
+ enableGPUValidationMode = "1"
65
+ allowLocationSimulation = "YES">
66
+ <BuildableProductRunnable
67
+ runnableDebuggingMode = "0">
68
+ <BuildableReference
69
+ BuildableIdentifier = "primary"
70
+ BlueprintIdentifier = "97C146ED1CF9000F007C117D"
71
+ BuildableName = "Runner.app"
72
+ BlueprintName = "Runner"
73
+ ReferencedContainer = "container:Runner.xcodeproj">
74
+ </BuildableReference>
75
+ </BuildableProductRunnable>
76
+ </LaunchAction>
77
+ <ProfileAction
78
+ buildConfiguration = "Profile"
79
+ shouldUseLaunchSchemeArgsEnv = "YES"
80
+ savedToolIdentifier = ""
81
+ useCustomWorkingDirectory = "NO"
82
+ debugDocumentVersioning = "YES">
83
+ <BuildableProductRunnable
84
+ runnableDebuggingMode = "0">
85
+ <BuildableReference
86
+ BuildableIdentifier = "primary"
87
+ BlueprintIdentifier = "97C146ED1CF9000F007C117D"
88
+ BuildableName = "Runner.app"
89
+ BlueprintName = "Runner"
90
+ ReferencedContainer = "container:Runner.xcodeproj">
91
+ </BuildableReference>
92
+ </BuildableProductRunnable>
93
+ </ProfileAction>
94
+ <AnalyzeAction
95
+ buildConfiguration = "Debug">
96
+ </AnalyzeAction>
97
+ <ArchiveAction
98
+ buildConfiguration = "Release"
99
+ revealArchiveInOrganizer = "YES">
100
+ </ArchiveAction>
101
+ </Scheme>
@@ -0,0 +1,7 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <Workspace
3
+ version = "1.0">
4
+ <FileRef
5
+ location = "group:Runner.xcodeproj">
6
+ </FileRef>
7
+ </Workspace>
@@ -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>IDEDidComputeMac32BitWarning</key>
6
+ <true/>
7
+ </dict>
8
+ </plist>
@@ -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>PreviewsEnabled</key>
6
+ <false/>
7
+ </dict>
8
+ </plist>
@@ -0,0 +1,12 @@
1
+ import Flutter
2
+ import UIKit
3
+ import XCTest
4
+
5
+ class RunnerTests: XCTestCase {
6
+
7
+ func testExample() {
8
+ // If you add code to the Runner application, consider adding tests here.
9
+ // See https://developer.apple.com/documentation/xctest for more information about using XCTest.
10
+ }
11
+
12
+ }
@@ -0,0 +1,153 @@
1
+ import 'package:flutter/material.dart';
2
+ import 'package:image_picker/image_picker.dart';
3
+ import '../main.dart';
4
+
5
+ class Avatar extends StatefulWidget {
6
+ const Avatar({
7
+ super.key,
8
+ this.imageUrl,
9
+ required this.size,
10
+ required this.onUpload,
11
+ });
12
+
13
+ final String? imageUrl;
14
+ final double size;
15
+ final void Function(String) onUpload;
16
+
17
+ @override
18
+ State<Avatar> createState() => _AvatarState();
19
+ }
20
+
21
+ class _AvatarState extends State<Avatar> {
22
+ bool _isLoading = false;
23
+ String? _imageUrl;
24
+
25
+ @override
26
+ void initState() {
27
+ super.initState();
28
+ _imageUrl = widget.imageUrl;
29
+ if (_imageUrl != null && _imageUrl!.isNotEmpty) {
30
+ _downloadImage(_imageUrl!);
31
+ }
32
+ }
33
+
34
+ @override
35
+ void didUpdateWidget(Avatar oldWidget) {
36
+ super.didUpdateWidget(oldWidget);
37
+ if (widget.imageUrl != oldWidget.imageUrl && widget.imageUrl != null) {
38
+ _downloadImage(widget.imageUrl!);
39
+ }
40
+ }
41
+
42
+ Future<void> _downloadImage(String path) async {
43
+ try {
44
+ final bytes = await supabase.storage.from('avatars').download(path);
45
+ if (mounted) {
46
+ setState(() {
47
+ _imageUrl = 'data:image/jpeg;base64,$bytes';
48
+ });
49
+ }
50
+ } catch (error) {
51
+ if (mounted) {
52
+ context.showSnackBar('下载头像失败', isError: true);
53
+ }
54
+ }
55
+ }
56
+
57
+ Future<void> _upload() async {
58
+ try {
59
+ final picker = ImagePicker();
60
+ final imageFile = await picker.pickImage(source: ImageSource.gallery);
61
+ if (imageFile == null) {
62
+ return;
63
+ }
64
+
65
+ setState(() {
66
+ _isLoading = true;
67
+ });
68
+
69
+ final bytes = await imageFile.readAsBytes();
70
+ final fileExt = imageFile.path.split('.').last;
71
+ final fileName = '${DateTime.now().millisecondsSinceEpoch}.$fileExt';
72
+
73
+ await supabase.storage.from('avatars').uploadBinary(fileName, bytes);
74
+
75
+ if (mounted) {
76
+ widget.onUpload(fileName);
77
+ }
78
+ } catch (error) {
79
+ if (mounted) {
80
+ context.showSnackBar('上传头像失败', isError: true);
81
+ }
82
+ } finally {
83
+ if (mounted) {
84
+ setState(() {
85
+ _isLoading = false;
86
+ });
87
+ }
88
+ }
89
+ }
90
+
91
+ @override
92
+ Widget build(BuildContext context) {
93
+ return Column(
94
+ children: [
95
+ SizedBox(
96
+ width: widget.size,
97
+ height: widget.size,
98
+ child: GestureDetector(
99
+ onTap: _upload,
100
+ child: Stack(
101
+ children: [
102
+ Container(
103
+ decoration: BoxDecoration(
104
+ shape: BoxShape.circle,
105
+ color: Theme.of(context).colorScheme.surfaceContainerHighest,
106
+ ),
107
+ child: Center(
108
+ child: _isLoading
109
+ ? const CircularProgressIndicator()
110
+ : _imageUrl != null && _imageUrl!.isNotEmpty
111
+ ? ClipOval(
112
+ child: Image.network(
113
+ _imageUrl!,
114
+ width: widget.size,
115
+ height: widget.size,
116
+ fit: BoxFit.cover,
117
+ errorBuilder: (context, error, stackTrace) {
118
+ return const Icon(Icons.person, size: 50);
119
+ },
120
+ ),
121
+ )
122
+ : const Icon(Icons.person, size: 50),
123
+ ),
124
+ ),
125
+ Positioned(
126
+ bottom: 0,
127
+ right: 0,
128
+ child: Container(
129
+ decoration: BoxDecoration(
130
+ shape: BoxShape.circle,
131
+ color: Theme.of(context).colorScheme.primary,
132
+ ),
133
+ padding: const EdgeInsets.all(4),
134
+ child: Icon(
135
+ Icons.camera_alt,
136
+ size: widget.size * 0.15,
137
+ color: Theme.of(context).colorScheme.onPrimary,
138
+ ),
139
+ ),
140
+ ),
141
+ ],
142
+ ),
143
+ ),
144
+ ),
145
+ const SizedBox(height: 8),
146
+ Text(
147
+ '点击更换头像',
148
+ style: Theme.of(context).textTheme.bodySmall,
149
+ ),
150
+ ],
151
+ );
152
+ }
153
+ }
@@ -0,0 +1,70 @@
1
+ import 'package:flutter/material.dart';
2
+ import 'package:supabase_flutter/supabase_flutter.dart';
3
+ import 'pages/login_page.dart';
4
+ import 'pages/account_page.dart';
5
+
6
+ void main() async {
7
+ WidgetsFlutterBinding.ensureInitialized();
8
+
9
+ await Supabase.initialize(
10
+ url: const String.fromEnvironment('SUPABASE_URL', defaultValue: ''),
11
+ anonKey: const String.fromEnvironment('SUPABASE_ANON_KEY', defaultValue: ''),
12
+ );
13
+
14
+ runApp(const MyApp());
15
+ }
16
+
17
+ final supabase = Supabase.instance.client;
18
+
19
+ class MyApp extends StatelessWidget {
20
+ const MyApp({super.key});
21
+
22
+ @override
23
+ Widget build(BuildContext context) {
24
+ return MaterialApp(
25
+ title: 'Supabase Flutter',
26
+ debugShowCheckedModeBanner: false,
27
+ theme: ThemeData(
28
+ colorScheme: ColorScheme.fromSeed(
29
+ seedColor: Colors.green,
30
+ brightness: Brightness.light,
31
+ ),
32
+ useMaterial3: true,
33
+ elevatedButtonTheme: ElevatedButtonThemeData(
34
+ style: ElevatedButton.styleFrom(
35
+ foregroundColor: Colors.white,
36
+ backgroundColor: Colors.green,
37
+ ),
38
+ ),
39
+ inputDecorationTheme: const InputDecorationTheme(
40
+ border: OutlineInputBorder(),
41
+ filled: true,
42
+ ),
43
+ ),
44
+ darkTheme: ThemeData(
45
+ colorScheme: ColorScheme.fromSeed(
46
+ seedColor: Colors.green,
47
+ brightness: Brightness.dark,
48
+ ),
49
+ useMaterial3: true,
50
+ ),
51
+ home: supabase.auth.currentSession == null
52
+ ? const LoginPage()
53
+ : const AccountPage(),
54
+ );
55
+ }
56
+ }
57
+
58
+ extension ContextExtension on BuildContext {
59
+ void showSnackBar(String message, {bool isError = false}) {
60
+ ScaffoldMessenger.of(this).showSnackBar(
61
+ SnackBar(
62
+ content: Text(message),
63
+ backgroundColor: isError
64
+ ? Theme.of(this).colorScheme.error
65
+ : Theme.of(this).snackBarTheme.backgroundColor,
66
+ behavior: SnackBarBehavior.floating,
67
+ ),
68
+ );
69
+ }
70
+ }
@@ -0,0 +1,189 @@
1
+ import 'package:flutter/material.dart';
2
+ import 'package:supabase_flutter/supabase_flutter.dart';
3
+ import '../main.dart';
4
+ import 'login_page.dart';
5
+ import '../components/avatar.dart';
6
+
7
+ class AccountPage extends StatefulWidget {
8
+ const AccountPage({super.key});
9
+
10
+ @override
11
+ State<AccountPage> createState() => _AccountPageState();
12
+ }
13
+
14
+ class _AccountPageState extends State<AccountPage> {
15
+ final _usernameController = TextEditingController();
16
+ final _fullNameController = TextEditingController();
17
+ final _websiteController = TextEditingController();
18
+
19
+ String? _avatarUrl;
20
+ var _loading = true;
21
+
22
+ Future<void> _getProfile() async {
23
+ setState(() {
24
+ _loading = true;
25
+ });
26
+
27
+ try {
28
+ final userId = supabase.auth.currentSession!.user.id;
29
+ final data = await supabase
30
+ .from('profiles')
31
+ .select()
32
+ .eq('id', userId)
33
+ .single();
34
+ _usernameController.text = (data['username'] ?? '') as String;
35
+ _fullNameController.text = (data['full_name'] ?? '') as String;
36
+ _websiteController.text = (data['website'] ?? '') as String;
37
+ _avatarUrl = (data['avatar_url'] ?? '') as String;
38
+ } on PostgrestException catch (error) {
39
+ if (mounted) context.showSnackBar(error.message, isError: true);
40
+ } catch (error) {
41
+ if (mounted) {
42
+ context.showSnackBar('发生意外错误', isError: true);
43
+ }
44
+ } finally {
45
+ if (mounted) {
46
+ setState(() {
47
+ _loading = false;
48
+ });
49
+ }
50
+ }
51
+ }
52
+
53
+ Future<void> _updateProfile() async {
54
+ setState(() {
55
+ _loading = true;
56
+ });
57
+ final userName = _usernameController.text.trim();
58
+ final fullName = _fullNameController.text.trim();
59
+ final website = _websiteController.text.trim();
60
+ final user = supabase.auth.currentUser;
61
+ final updates = {
62
+ 'id': user!.id,
63
+ 'username': userName,
64
+ 'full_name': fullName,
65
+ 'website': website,
66
+ 'updated_at': DateTime.now().toIso8601String(),
67
+ };
68
+ try {
69
+ await supabase.from('profiles').upsert(updates);
70
+ if (mounted) context.showSnackBar('资料更新成功!');
71
+ } on PostgrestException catch (error) {
72
+ if (mounted) context.showSnackBar(error.message, isError: true);
73
+ } catch (error) {
74
+ if (mounted) {
75
+ context.showSnackBar('发生意外错误', isError: true);
76
+ }
77
+ } finally {
78
+ if (mounted) {
79
+ setState(() {
80
+ _loading = false;
81
+ });
82
+ }
83
+ }
84
+ }
85
+
86
+ Future<void> _signOut() async {
87
+ try {
88
+ await supabase.auth.signOut();
89
+ } on AuthException catch (error) {
90
+ if (mounted) context.showSnackBar(error.message, isError: true);
91
+ } catch (error) {
92
+ if (mounted) {
93
+ context.showSnackBar('发生意外错误', isError: true);
94
+ }
95
+ } finally {
96
+ if (mounted) {
97
+ Navigator.of(context).pushReplacement(
98
+ MaterialPageRoute(builder: (_) => const LoginPage()),
99
+ );
100
+ }
101
+ }
102
+ }
103
+
104
+ @override
105
+ void initState() {
106
+ super.initState();
107
+ _getProfile();
108
+ }
109
+
110
+ @override
111
+ void dispose() {
112
+ _usernameController.dispose();
113
+ _fullNameController.dispose();
114
+ _websiteController.dispose();
115
+ super.dispose();
116
+ }
117
+
118
+ @override
119
+ Widget build(BuildContext context) {
120
+ return Scaffold(
121
+ appBar: AppBar(
122
+ title: const Text('账户设置'),
123
+ centerTitle: true,
124
+ ),
125
+ body: ListView(
126
+ padding: const EdgeInsets.symmetric(vertical: 24, horizontal: 24),
127
+ children: [
128
+ if (_loading && _avatarUrl == null)
129
+ const Center(child: CircularProgressIndicator())
130
+ else
131
+ Avatar(
132
+ imageUrl: _avatarUrl,
133
+ size: 100,
134
+ onUpload: (String url) {
135
+ setState(() {
136
+ _avatarUrl = url;
137
+ });
138
+ _updateProfile();
139
+ },
140
+ ),
141
+ const SizedBox(height: 24),
142
+ Center(
143
+ child: Text(
144
+ supabase.auth.currentUser?.email ?? '',
145
+ style: Theme.of(context).textTheme.bodyMedium?.copyWith(
146
+ color: Theme.of(context).colorScheme.onSurfaceVariant,
147
+ ),
148
+ ),
149
+ ),
150
+ const SizedBox(height: 32),
151
+ TextFormField(
152
+ controller: _usernameController,
153
+ decoration: const InputDecoration(
154
+ labelText: '用户名',
155
+ prefixIcon: Icon(Icons.person),
156
+ ),
157
+ ),
158
+ const SizedBox(height: 16),
159
+ TextFormField(
160
+ controller: _fullNameController,
161
+ decoration: const InputDecoration(
162
+ labelText: '姓名',
163
+ prefixIcon: Icon(Icons.badge),
164
+ ),
165
+ ),
166
+ const SizedBox(height: 16),
167
+ TextFormField(
168
+ controller: _websiteController,
169
+ decoration: const InputDecoration(
170
+ labelText: '网站',
171
+ prefixIcon: Icon(Icons.link),
172
+ ),
173
+ keyboardType: TextInputType.url,
174
+ ),
175
+ const SizedBox(height: 24),
176
+ ElevatedButton(
177
+ onPressed: _loading ? null : _updateProfile,
178
+ child: Text(_loading ? '保存中...' : '更新资料'),
179
+ ),
180
+ const SizedBox(height: 16),
181
+ TextButton(
182
+ onPressed: _signOut,
183
+ child: const Text('退出登录'),
184
+ ),
185
+ ],
186
+ ),
187
+ );
188
+ }
189
+ }
@@ -0,0 +1,150 @@
1
+ import 'dart:async';
2
+
3
+ import 'package:flutter/material.dart';
4
+ import 'package:supabase_flutter/supabase_flutter.dart';
5
+ import '../main.dart';
6
+ import 'account_page.dart';
7
+
8
+ class LoginPage extends StatefulWidget {
9
+ const LoginPage({super.key});
10
+
11
+ @override
12
+ State<LoginPage> createState() => _LoginPageState();
13
+ }
14
+
15
+ class _LoginPageState extends State<LoginPage> {
16
+ bool _isLoading = false;
17
+ bool _redirecting = false;
18
+ late final TextEditingController _emailController = TextEditingController();
19
+ late final TextEditingController _passwordController = TextEditingController();
20
+ late final StreamSubscription<AuthState> _authStateSubscription;
21
+
22
+ Future<void> _signIn() async {
23
+ setState(() {
24
+ _isLoading = true;
25
+ });
26
+ try {
27
+ await supabase.auth.signInWithPassword(
28
+ email: _emailController.text.trim(),
29
+ password: _passwordController.text.trim(),
30
+ );
31
+ } on AuthException catch (error) {
32
+ if (mounted) context.showSnackBar(error.message, isError: true);
33
+ } catch (error) {
34
+ if (mounted) {
35
+ context.showSnackBar('发生意外错误', isError: true);
36
+ }
37
+ } finally {
38
+ if (mounted) {
39
+ setState(() {
40
+ _isLoading = false;
41
+ });
42
+ }
43
+ }
44
+ }
45
+
46
+ Future<void> _signUp() async {
47
+ setState(() {
48
+ _isLoading = true;
49
+ });
50
+ try {
51
+ await supabase.auth.signUp(
52
+ email: _emailController.text.trim(),
53
+ password: _passwordController.text.trim(),
54
+ );
55
+ if (mounted) {
56
+ context.showSnackBar('注册成功!请检查邮箱验证');
57
+ _emailController.clear();
58
+ _passwordController.clear();
59
+ }
60
+ } on AuthException catch (error) {
61
+ if (mounted) context.showSnackBar(error.message, isError: true);
62
+ } catch (error) {
63
+ if (mounted) {
64
+ context.showSnackBar('发生意外错误', isError: true);
65
+ }
66
+ } finally {
67
+ if (mounted) {
68
+ setState(() {
69
+ _isLoading = false;
70
+ });
71
+ }
72
+ }
73
+ }
74
+
75
+ @override
76
+ void initState() {
77
+ _authStateSubscription = supabase.auth.onAuthStateChange.listen((data) {
78
+ if (_redirecting) return;
79
+ final session = data.session;
80
+ if (session != null) {
81
+ _redirecting = true;
82
+ WidgetsBinding.instance.addPostFrameCallback((_) {
83
+ if (mounted) {
84
+ Navigator.of(context).pushReplacement(
85
+ MaterialPageRoute(builder: (context) => const AccountPage()),
86
+ );
87
+ }
88
+ });
89
+ }
90
+ });
91
+ super.initState();
92
+ }
93
+
94
+ @override
95
+ void dispose() {
96
+ _emailController.dispose();
97
+ _passwordController.dispose();
98
+ _authStateSubscription.cancel();
99
+ super.dispose();
100
+ }
101
+
102
+ @override
103
+ Widget build(BuildContext context) {
104
+ return Scaffold(
105
+ appBar: AppBar(
106
+ title: const Text('用户管理'),
107
+ centerTitle: true,
108
+ ),
109
+ body: ListView(
110
+ padding: const EdgeInsets.symmetric(vertical: 24, horizontal: 24),
111
+ children: [
112
+ const Text(
113
+ '登录或注册您的账户',
114
+ style: TextStyle(fontSize: 16),
115
+ ),
116
+ const SizedBox(height: 24),
117
+ TextFormField(
118
+ controller: _emailController,
119
+ decoration: const InputDecoration(
120
+ labelText: '邮箱',
121
+ prefixIcon: Icon(Icons.email),
122
+ ),
123
+ keyboardType: TextInputType.emailAddress,
124
+ textInputAction: TextInputAction.next,
125
+ ),
126
+ const SizedBox(height: 16),
127
+ TextFormField(
128
+ controller: _passwordController,
129
+ decoration: const InputDecoration(
130
+ labelText: '密码',
131
+ prefixIcon: Icon(Icons.lock),
132
+ ),
133
+ obscureText: true,
134
+ textInputAction: TextInputAction.done,
135
+ ),
136
+ const SizedBox(height: 24),
137
+ ElevatedButton(
138
+ onPressed: _isLoading ? null : _signIn,
139
+ child: Text(_isLoading ? '处理中...' : '登录'),
140
+ ),
141
+ const SizedBox(height: 12),
142
+ OutlinedButton(
143
+ onPressed: _isLoading ? null : _signUp,
144
+ child: const Text('注册'),
145
+ ),
146
+ ],
147
+ ),
148
+ );
149
+ }
150
+ }