├── .gitignore ├── .metadata ├── .vscode └── launch.json ├── LICENSE ├── Makefile ├── README.md ├── analysis_options.yaml ├── android ├── .gitignore ├── app │ ├── build.gradle │ └── src │ │ ├── debug │ │ └── AndroidManifest.xml │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── kotlin │ │ │ └── com │ │ │ │ └── example │ │ │ │ └── flutter_todo │ │ │ │ └── MainActivity.kt │ │ └── res │ │ │ ├── drawable-v21 │ │ │ └── launch_background.xml │ │ │ ├── drawable │ │ │ └── launch_background.xml │ │ │ ├── mipmap-hdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-mdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xhdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxhdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxxhdpi │ │ │ └── ic_launcher.png │ │ │ ├── values-night │ │ │ └── styles.xml │ │ │ └── values │ │ │ └── styles.xml │ │ └── profile │ │ └── AndroidManifest.xml ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties └── settings.gradle ├── build.yaml ├── docs └── cloud_firestore.md ├── firebase ├── .firebaserc ├── .gitignore ├── auth_export │ └── config.json ├── firebase-export-metadata.json ├── firebase.json ├── firestore.indexes.json └── firestore.rules ├── ios ├── .gitignore ├── Flutter │ ├── AppFrameworkInfo.plist │ ├── Debug.xcconfig │ └── Release.xcconfig ├── Podfile ├── Podfile.lock ├── Runner.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ └── WorkspaceSettings.xcsettings │ └── xcshareddata │ │ └── xcschemes │ │ └── Runner.xcscheme ├── Runner.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ └── WorkspaceSettings.xcsettings └── Runner │ ├── AppDelegate.swift │ ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ ├── Contents.json │ │ ├── Icon-App-1024x1024@1x.png │ │ ├── Icon-App-20x20@1x.png │ │ ├── Icon-App-20x20@2x.png │ │ ├── Icon-App-20x20@3x.png │ │ ├── Icon-App-29x29@1x.png │ │ ├── Icon-App-29x29@2x.png │ │ ├── Icon-App-29x29@3x.png │ │ ├── Icon-App-40x40@1x.png │ │ ├── Icon-App-40x40@2x.png │ │ ├── Icon-App-40x40@3x.png │ │ ├── Icon-App-60x60@2x.png │ │ ├── Icon-App-60x60@3x.png │ │ ├── Icon-App-76x76@1x.png │ │ ├── Icon-App-76x76@2x.png │ │ └── Icon-App-83.5x83.5@2x.png │ └── LaunchImage.imageset │ │ ├── Contents.json │ │ ├── LaunchImage.png │ │ ├── LaunchImage@2x.png │ │ ├── LaunchImage@3x.png │ │ └── README.md │ ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard │ ├── Info.plist │ └── Runner-Bridging-Header.h ├── lib ├── app.dart ├── config │ └── page_storage_keys.dart ├── firebase_options.dart ├── generated │ ├── model │ │ ├── form_model.freezed.dart │ │ ├── result.freezed.dart │ │ ├── task.freezed.dart │ │ ├── user.freezed.dart │ │ └── user_auth.freezed.dart │ ├── util │ │ ├── pagenated_list_controller.freezed.dart │ │ └── tupple.freezed.dart │ └── view │ │ └── page │ │ ├── create_task_page │ │ └── create_task_page_controller.freezed.dart │ │ ├── edit_task_page │ │ └── edit_task_page_controller.freezed.dart │ │ ├── register_page │ │ └── register_page_controller.freezed.dart │ │ ├── setting_page │ │ └── setting_page_controller.freezed.dart │ │ ├── signin_page │ │ └── signin_page_controller.freezed.dart │ │ └── signup_page │ │ └── signup_page_controller.freezed.dart ├── infrastructure │ ├── authenticator_provider.dart │ ├── cursor_impl.dart │ ├── firebase.dart │ ├── firestore_error.dart │ ├── task_repository_impl.dart │ └── user_repository_impl.dart ├── main.dart ├── model │ ├── app_error.dart │ ├── form_model.dart │ ├── query_list.dart │ ├── result.dart │ ├── task.dart │ ├── user.dart │ ├── user_auth.dart │ └── validator.dart ├── provider │ ├── global_controller │ │ ├── date_format_provider.dart │ │ ├── loading_provider.dart │ │ └── network_dialog_provider.dart │ ├── infrastructure │ │ └── user_provider.dart │ ├── local │ │ └── local_provider.dart │ ├── model │ │ ├── task_provider.dart │ │ ├── task_repository_provider.dart │ │ └── user_repository_provider.dart │ └── route │ │ ├── guard.dart │ │ ├── local_provider_scope.dart │ │ ├── pram.dart │ │ ├── router_provider.dart │ │ └── routes.dart ├── run.dart ├── util │ ├── async_value.dart │ ├── env.dart │ ├── logger.dart │ ├── pagenated_list_controller.dart │ ├── tupple.dart │ └── util.dart └── view │ ├── component │ ├── custom_modal_barriere.dart │ ├── error_view.dart │ ├── lazy_indexed_stack.dart │ ├── loading_overlay.dart │ ├── loading_view.dart │ ├── my_form.dart │ ├── not_found_view.dart │ └── task_list_item.dart │ ├── dialog │ └── network_alert_dialog.dart │ ├── page │ ├── create_task_page │ │ ├── create_task_page.dart │ │ └── create_task_page_controller.dart │ ├── debug_page │ │ └── debug_page.dart │ ├── edit_task_page │ │ ├── edit_task_page.dart │ │ └── edit_task_page_controller.dart │ ├── error_page │ │ ├── error_page.dart │ │ └── error_page_controller.dart │ ├── home_page │ │ ├── home_page.dart │ │ └── home_page_controller.dart │ ├── notfound_page │ │ ├── not_found_page_controller.dart │ │ └── notfound_page.dart │ ├── register_page │ │ ├── register_page.dart │ │ └── register_page_controller.dart │ ├── setting_page │ │ ├── setting_page.dart │ │ └── setting_page_controller.dart │ ├── signin_page │ │ ├── signin_page.dart │ │ └── signin_page_controller.dart │ ├── signup_page │ │ ├── signup_page.dart │ │ └── signup_page_controller.dart │ ├── task_detail_page │ │ ├── task_detail_page.dart │ │ └── task_detail_page_controller.dart │ └── task_tab │ │ ├── done_tab.dart │ │ ├── done_tab_controller.dart │ │ ├── todo_tab.dart │ │ └── todo_tab_controller.dart │ └── screen │ ├── mypage_screen │ ├── mypage_screen.dart │ └── mypage_screen_controller.dart │ └── task_screen │ ├── task_screen.dart │ └── task_screen_controller.dart ├── pubspec.lock ├── pubspec.yaml └── web ├── favicon.png ├── icons ├── Icon-192.png ├── Icon-512.png ├── Icon-maskable-192.png └── Icon-maskable-512.png ├── index.html └── manifest.json /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # The .vscode folder contains launch configuration and tasks you configure in 19 | # VS Code which you may wish to be included in version control, so this line 20 | # is commented out by default. 21 | #.vscode/ 22 | 23 | # Flutter/Dart/Pub related 24 | **/doc/api/ 25 | **/ios/Flutter/.last_build_id 26 | .dart_tool/ 27 | .flutter-plugins 28 | .flutter-plugins-dependencies 29 | .packages 30 | .pub-cache/ 31 | .pub/ 32 | /build/ 33 | 34 | # Web related 35 | lib/generated_plugin_registrant.dart 36 | 37 | # Symbolication related 38 | app.*.symbols 39 | 40 | # Obfuscation related 41 | app.*.map.json 42 | 43 | # Android Studio will place build artifacts here 44 | /android/app/debug 45 | /android/app/profile 46 | /android/app/release 47 | -------------------------------------------------------------------------------- /.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: 77d935af4db863f6abd0b9c31c7e6df2a13de57b 8 | channel: stable 9 | 10 | project_type: app 11 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "flutter_todo", 9 | "request": "launch", 10 | "type": "dart" 11 | }, 12 | { 13 | "name": "flutter_todo (profile mode)", 14 | "request": "launch", 15 | "type": "dart", 16 | "flutterMode": "profile", 17 | } 18 | ] 19 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 torikatsu 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: run 2 | run: start_emulator 3 | flutter run 4 | 5 | .PHONY: run_web_server 6 | run_web_server: 7 | flutter run -d web-server 8 | 9 | .PHONY: run_with_real_firebase 10 | run_with_real_firebase: 11 | flutter run -d web-server --dart-define=USE_FIREBASE_EMULATOR=false 12 | 13 | .PHONY: start_emulator 14 | start_emulator: 15 | cd firebase && firebase emulators:start --import . --export-on-exit 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # flutter_todo 2 | 3 | A new Flutter project. 4 | 5 | ## Getting Started 6 | 7 | This project is a starting point for a Flutter application. 8 | 9 | A few resources to get you started if this is your first Flutter project: 10 | 11 | - [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab) 12 | - [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook) 13 | 14 | For help getting started with Flutter, view our 15 | [online documentation](https://flutter.dev/docs), which offers tutorials, 16 | samples, guidance on mobile development, and a full API reference. 17 | -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | analyzer: 2 | errors: 3 | invalid_annotation_target: ignore 4 | todo: ignore 5 | include: package:flutter_lints/flutter.yaml 6 | exclude: 7 | - "**/*.g.dart" 8 | - "**/*.freezed.dart" 9 | linter: 10 | rules: -------------------------------------------------------------------------------- /android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | /local.properties 7 | GeneratedPluginRegistrant.java 8 | 9 | # Remember to never publicly share your keystore. 10 | # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app 11 | key.properties 12 | **/*.keystore 13 | **/*.jks 14 | -------------------------------------------------------------------------------- /android/app/build.gradle: -------------------------------------------------------------------------------- 1 | def localProperties = new Properties() 2 | def localPropertiesFile = rootProject.file('local.properties') 3 | if (localPropertiesFile.exists()) { 4 | localPropertiesFile.withReader('UTF-8') { reader -> 5 | localProperties.load(reader) 6 | } 7 | } 8 | 9 | def flutterRoot = localProperties.getProperty('flutter.sdk') 10 | if (flutterRoot == null) { 11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") 12 | } 13 | 14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 15 | if (flutterVersionCode == null) { 16 | flutterVersionCode = '1' 17 | } 18 | 19 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 20 | if (flutterVersionName == null) { 21 | flutterVersionName = '1.0' 22 | } 23 | 24 | apply plugin: 'com.android.application' 25 | apply plugin: 'kotlin-android' 26 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 27 | 28 | android { 29 | compileSdkVersion flutter.compileSdkVersion 30 | 31 | compileOptions { 32 | sourceCompatibility JavaVersion.VERSION_1_8 33 | targetCompatibility JavaVersion.VERSION_1_8 34 | } 35 | 36 | kotlinOptions { 37 | jvmTarget = '1.8' 38 | } 39 | 40 | sourceSets { 41 | main.java.srcDirs += 'src/main/kotlin' 42 | } 43 | 44 | defaultConfig { 45 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 46 | applicationId "com.example.flutter_todo" 47 | minSdkVersion 21 48 | targetSdkVersion flutter.targetSdkVersion 49 | versionCode flutterVersionCode.toInteger() 50 | versionName flutterVersionName 51 | } 52 | 53 | buildTypes { 54 | release { 55 | // TODO: Add your own signing config for the release build. 56 | // Signing with the debug keys for now, so `flutter run --release` works. 57 | signingConfig signingConfigs.debug 58 | } 59 | } 60 | } 61 | 62 | flutter { 63 | source '../..' 64 | } 65 | 66 | dependencies { 67 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 68 | } 69 | -------------------------------------------------------------------------------- /android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | > 8 | 17 | 21 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 43 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /android/app/src/main/kotlin/com/example/flutter_todo/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.example.flutter_todo 2 | 3 | import io.flutter.embedding.android.FlutterActivity 4 | 5 | class MainActivity: FlutterActivity() { 6 | } 7 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-v21/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/torikatsupg/flutter_todo/a8286ca196bbb52ea4797d2273662e0665879af2/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/torikatsupg/flutter_todo/a8286ca196bbb52ea4797d2273662e0665879af2/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/torikatsupg/flutter_todo/a8286ca196bbb52ea4797d2273662e0665879af2/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/torikatsupg/flutter_todo/a8286ca196bbb52ea4797d2273662e0665879af2/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/torikatsupg/flutter_todo/a8286ca196bbb52ea4797d2273662e0665879af2/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/values-night/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.6.10' 3 | repositories { 4 | google() 5 | mavenCentral() 6 | } 7 | 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:4.1.0' 10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 11 | } 12 | } 13 | 14 | allprojects { 15 | repositories { 16 | google() 17 | mavenCentral() 18 | } 19 | } 20 | 21 | rootProject.buildDir = '../build' 22 | subprojects { 23 | project.buildDir = "${rootProject.buildDir}/${project.name}" 24 | } 25 | subprojects { 26 | project.evaluationDependsOn(':app') 27 | } 28 | 29 | task clean(type: Delete) { 30 | delete rootProject.buildDir 31 | } 32 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.useAndroidX=true 3 | android.enableJetifier=true 4 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Jun 23 08:50:38 CEST 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip 7 | -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | 3 | def localPropertiesFile = new File(rootProject.projectDir, "local.properties") 4 | def properties = new Properties() 5 | 6 | assert localPropertiesFile.exists() 7 | localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } 8 | 9 | def flutterSdkPath = properties.getProperty("flutter.sdk") 10 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties" 11 | apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" 12 | -------------------------------------------------------------------------------- /build.yaml: -------------------------------------------------------------------------------- 1 | targets: 2 | $default: 3 | builders: 4 | source_gen|combining_builder: 5 | options: 6 | build_extensions: 7 | '^lib/{{}}.dart': 'lib/generated/{{}}.g.dart' 8 | freezed: 9 | generate_for: 10 | include: 11 | - 'lib/model/**.dart' 12 | - 'lib/provider/**.dart' 13 | - 'lib/firebase/**.dart' 14 | - 'lib/util/**.dart' 15 | - 'lib/view/**.dart' 16 | options: 17 | build_extensions: 18 | '^lib/{{}}.dart': 'lib/generated/{{}}.freezed.dart' 19 | # explicit_to_json: true -------------------------------------------------------------------------------- /docs/cloud_firestore.md: -------------------------------------------------------------------------------- 1 | # CloudFirestore schema 2 | ## root 3 | ```json 4 | { 5 | users: [ 6 | $userId: { 7 | // field 8 | username: String, 9 | // collection 10 | tasks: [ 11 | $taskId: { 12 | name: String, 13 | createdAt: date?, 14 | isDone: boolean, 15 | }, 16 | ... 17 | ] 18 | }, 19 | ... 20 | ] 21 | } 22 | ``` 23 | -------------------------------------------------------------------------------- /firebase/.firebaserc: -------------------------------------------------------------------------------- 1 | { 2 | "projects": { 3 | "default": "flutter-architecture-11ca3" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /firebase/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | firebase-debug.log* 8 | firebase-debug.*.log* 9 | 10 | # Firebase cache 11 | .firebase/ 12 | 13 | # Firebase config 14 | 15 | # Uncomment this if you'd like others to create their own Firebase project. 16 | # For a team working on the same Firebase project(s), it is recommended to leave 17 | # it commented so all members can deploy to the same project(s) in .firebaserc. 18 | # .firebaserc 19 | 20 | # Runtime data 21 | pids 22 | *.pid 23 | *.seed 24 | *.pid.lock 25 | 26 | # Directory for instrumented libs generated by jscoverage/JSCover 27 | lib-cov 28 | 29 | # Coverage directory used by tools like istanbul 30 | coverage 31 | 32 | # nyc test coverage 33 | .nyc_output 34 | 35 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 36 | .grunt 37 | 38 | # Bower dependency directory (https://bower.io/) 39 | bower_components 40 | 41 | # node-waf configuration 42 | .lock-wscript 43 | 44 | # Compiled binary addons (http://nodejs.org/api/addons.html) 45 | build/Release 46 | 47 | # Dependency directories 48 | node_modules/ 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Optional REPL history 57 | .node_repl_history 58 | 59 | # Output of 'npm pack' 60 | *.tgz 61 | 62 | # Yarn Integrity file 63 | .yarn-integrity 64 | 65 | # dotenv environment variables file 66 | .env 67 | 68 | # emulator 69 | firestore_export/** 70 | auth_export/** -------------------------------------------------------------------------------- /firebase/auth_export/config.json: -------------------------------------------------------------------------------- 1 | {"signIn":{"allowDuplicateEmails":false}} -------------------------------------------------------------------------------- /firebase/firebase-export-metadata.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "9.12.1", 3 | "firestore": { 4 | "version": "1.12.0", 5 | "path": "firestore_export", 6 | "metadata_file": "firestore_export/firestore_export.overall_export_metadata" 7 | }, 8 | "auth": { 9 | "version": "9.12.1", 10 | "path": "auth_export" 11 | } 12 | } -------------------------------------------------------------------------------- /firebase/firebase.json: -------------------------------------------------------------------------------- 1 | { 2 | "firestore": { 3 | "rules": "firestore.rules", 4 | "indexes": "firestore.indexes.json" 5 | }, 6 | "emulators": { 7 | "auth": { 8 | "port": 9099 9 | }, 10 | "firestore": { 11 | "port": 8080 12 | }, 13 | "ui": { 14 | "enabled": true 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /firebase/firestore.indexes.json: -------------------------------------------------------------------------------- 1 | { 2 | "indexes": [], 3 | "fieldOverrides": [] 4 | } 5 | -------------------------------------------------------------------------------- /firebase/firestore.rules: -------------------------------------------------------------------------------- 1 | rules_version = '2'; 2 | service cloud.firestore { 3 | match /databases/{database}/documents { 4 | match /{document=**} { 5 | allow read, write: if 6 | request.time < timestamp.date(2023, 6, 1); 7 | } 8 | } 9 | } -------------------------------------------------------------------------------- /ios/.gitignore: -------------------------------------------------------------------------------- 1 | **/dgph 2 | *.mode1v3 3 | *.mode2v3 4 | *.moved-aside 5 | *.pbxuser 6 | *.perspectivev3 7 | **/*sync/ 8 | .sconsign.dblite 9 | .tags* 10 | **/.vagrant/ 11 | **/DerivedData/ 12 | Icon? 13 | **/Pods/ 14 | **/.symlinks/ 15 | profile 16 | xcuserdata 17 | **/.generated/ 18 | Flutter/App.framework 19 | Flutter/Flutter.framework 20 | Flutter/Flutter.podspec 21 | Flutter/Generated.xcconfig 22 | Flutter/ephemeral/ 23 | Flutter/app.flx 24 | Flutter/app.zip 25 | Flutter/flutter_assets/ 26 | Flutter/flutter_export_environment.sh 27 | ServiceDefinitions.json 28 | Runner/GeneratedPluginRegistrant.* 29 | 30 | # Exceptions to above rules. 31 | !default.mode1v3 32 | !default.mode2v3 33 | !default.pbxuser 34 | !default.perspectivev3 35 | -------------------------------------------------------------------------------- /ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 9.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /ios/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | platform :ios, '10.0' 3 | 4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 6 | 7 | project 'Runner', { 8 | 'Debug' => :debug, 9 | 'Profile' => :release, 10 | 'Release' => :release, 11 | } 12 | 13 | def flutter_root 14 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) 15 | unless File.exist?(generated_xcode_build_settings_path) 16 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" 17 | end 18 | 19 | File.foreach(generated_xcode_build_settings_path) do |line| 20 | matches = line.match(/FLUTTER_ROOT\=(.*)/) 21 | return matches[1].strip if matches 22 | end 23 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" 24 | end 25 | 26 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) 27 | 28 | flutter_ios_podfile_setup 29 | 30 | target 'Runner' do 31 | pod 'FirebaseFirestore', :git => 'https://github.com/invertase/firestore-ios-sdk-frameworks.git', :tag => '9.3.0' 32 | 33 | use_frameworks! 34 | use_modular_headers! 35 | 36 | flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) 37 | end 38 | 39 | post_install do |installer| 40 | installer.pods_project.targets.each do |target| 41 | flutter_additional_ios_build_settings(target) 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /ios/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - cloud_firestore (3.4.4): 3 | - Firebase/Firestore (= 9.3.0) 4 | - firebase_core 5 | - Flutter 6 | - Firebase/Auth (9.3.0): 7 | - Firebase/CoreOnly 8 | - FirebaseAuth (~> 9.3.0) 9 | - Firebase/CoreOnly (9.3.0): 10 | - FirebaseCore (= 9.3.0) 11 | - Firebase/Firestore (9.3.0): 12 | - Firebase/CoreOnly 13 | - FirebaseFirestore (~> 9.3.0) 14 | - firebase_auth (3.6.3): 15 | - Firebase/Auth (= 9.3.0) 16 | - firebase_core 17 | - Flutter 18 | - firebase_core (1.20.1): 19 | - Firebase/CoreOnly (= 9.3.0) 20 | - Flutter 21 | - FirebaseAuth (9.3.0): 22 | - FirebaseCore (~> 9.0) 23 | - GoogleUtilities/AppDelegateSwizzler (~> 7.7) 24 | - GoogleUtilities/Environment (~> 7.7) 25 | - GTMSessionFetcher/Core (< 3.0, >= 1.7) 26 | - FirebaseCore (9.3.0): 27 | - FirebaseCoreDiagnostics (~> 9.0) 28 | - FirebaseCoreInternal (~> 9.0) 29 | - GoogleUtilities/Environment (~> 7.7) 30 | - GoogleUtilities/Logger (~> 7.7) 31 | - FirebaseCoreDiagnostics (9.3.0): 32 | - GoogleDataTransport (< 10.0.0, >= 9.1.4) 33 | - GoogleUtilities/Environment (~> 7.7) 34 | - GoogleUtilities/Logger (~> 7.7) 35 | - nanopb (< 2.30910.0, >= 2.30908.0) 36 | - FirebaseCoreInternal (9.3.0): 37 | - "GoogleUtilities/NSData+zlib (~> 7.7)" 38 | - FirebaseFirestore (9.3.0): 39 | - FirebaseFirestore/AutodetectLeveldb (= 9.3.0) 40 | - FirebaseFirestore/AutodetectLeveldb (9.3.0): 41 | - FirebaseFirestore/Base 42 | - FirebaseFirestore/WithLeveldb 43 | - FirebaseFirestore/Base (9.3.0) 44 | - FirebaseFirestore/WithLeveldb (9.3.0): 45 | - FirebaseFirestore/Base 46 | - Flutter (1.0.0) 47 | - GoogleDataTransport (9.2.0): 48 | - GoogleUtilities/Environment (~> 7.7) 49 | - nanopb (< 2.30910.0, >= 2.30908.0) 50 | - PromisesObjC (< 3.0, >= 1.2) 51 | - GoogleUtilities/AppDelegateSwizzler (7.7.0): 52 | - GoogleUtilities/Environment 53 | - GoogleUtilities/Logger 54 | - GoogleUtilities/Network 55 | - GoogleUtilities/Environment (7.7.0): 56 | - PromisesObjC (< 3.0, >= 1.2) 57 | - GoogleUtilities/Logger (7.7.0): 58 | - GoogleUtilities/Environment 59 | - GoogleUtilities/Network (7.7.0): 60 | - GoogleUtilities/Logger 61 | - "GoogleUtilities/NSData+zlib" 62 | - GoogleUtilities/Reachability 63 | - "GoogleUtilities/NSData+zlib (7.7.0)" 64 | - GoogleUtilities/Reachability (7.7.0): 65 | - GoogleUtilities/Logger 66 | - GTMSessionFetcher/Core (2.0.0) 67 | - nanopb (2.30909.0): 68 | - nanopb/decode (= 2.30909.0) 69 | - nanopb/encode (= 2.30909.0) 70 | - nanopb/decode (2.30909.0) 71 | - nanopb/encode (2.30909.0) 72 | - PromisesObjC (2.1.1) 73 | 74 | DEPENDENCIES: 75 | - cloud_firestore (from `.symlinks/plugins/cloud_firestore/ios`) 76 | - firebase_auth (from `.symlinks/plugins/firebase_auth/ios`) 77 | - firebase_core (from `.symlinks/plugins/firebase_core/ios`) 78 | - FirebaseFirestore (from `https://github.com/invertase/firestore-ios-sdk-frameworks.git`, tag `9.3.0`) 79 | - Flutter (from `Flutter`) 80 | 81 | SPEC REPOS: 82 | trunk: 83 | - Firebase 84 | - FirebaseAuth 85 | - FirebaseCore 86 | - FirebaseCoreDiagnostics 87 | - FirebaseCoreInternal 88 | - GoogleDataTransport 89 | - GoogleUtilities 90 | - GTMSessionFetcher 91 | - nanopb 92 | - PromisesObjC 93 | 94 | EXTERNAL SOURCES: 95 | cloud_firestore: 96 | :path: ".symlinks/plugins/cloud_firestore/ios" 97 | firebase_auth: 98 | :path: ".symlinks/plugins/firebase_auth/ios" 99 | firebase_core: 100 | :path: ".symlinks/plugins/firebase_core/ios" 101 | FirebaseFirestore: 102 | :git: https://github.com/invertase/firestore-ios-sdk-frameworks.git 103 | :tag: 9.3.0 104 | Flutter: 105 | :path: Flutter 106 | 107 | CHECKOUT OPTIONS: 108 | FirebaseFirestore: 109 | :git: https://github.com/invertase/firestore-ios-sdk-frameworks.git 110 | :tag: 9.3.0 111 | 112 | SPEC CHECKSUMS: 113 | cloud_firestore: 683679d43f60e55f9318a5cb03b13637e060a99c 114 | Firebase: ef75abb1cdbc746d5a38f4e26c422c807b189b8c 115 | firebase_auth: 3726552033ccc757ef90f046ff2bcf5a13cd34bd 116 | firebase_core: e66a443ec996cb5e364dc70b4cfc1809c32cbb2e 117 | FirebaseAuth: 9ebc3577fe0acf9092df21ac314024b70aebf21e 118 | FirebaseCore: c088995ece701a021a48a1348ea0174877de2a6a 119 | FirebaseCoreDiagnostics: 060eb57cc56dfaf40b1b1b5874a5c17c41ce79f8 120 | FirebaseCoreInternal: 635d1c9a612a6502b6377a0c92af83758076ffff 121 | FirebaseFirestore: 46c6a5f8f633267208ff16e6b72d7aedc7e77c8d 122 | Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a 123 | GoogleDataTransport: 1c8145da7117bd68bbbed00cf304edb6a24de00f 124 | GoogleUtilities: e0913149f6b0625b553d70dae12b49fc62914fd1 125 | GTMSessionFetcher: 681175626052e03fdde7952f7e9c7a9785719506 126 | nanopb: b552cce312b6c8484180ef47159bc0f65a1f0431 127 | PromisesObjC: ab77feca74fa2823e7af4249b8326368e61014cb 128 | 129 | PODFILE CHECKSUM: ab723b5e1cc5ae4d6d0af4202e15bb1c3c4b65d1 130 | 131 | COCOAPODS: 1.11.3 132 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 37 | 38 | 39 | 40 | 41 | 42 | 52 | 54 | 60 | 61 | 62 | 63 | 69 | 71 | 77 | 78 | 79 | 80 | 82 | 83 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import Flutter 3 | 4 | @UIApplicationMain 5 | @objc class AppDelegate: FlutterAppDelegate { 6 | override func application( 7 | _ application: UIApplication, 8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 9 | ) -> Bool { 10 | GeneratedPluginRegistrant.register(with: self) 11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-App-20x20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-App-20x20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-App-29x29@1x.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-App-29x29@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-App-29x29@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-App-40x40@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-App-40x40@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-App-60x60@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "60x60", 53 | "idiom" : "iphone", 54 | "filename" : "Icon-App-60x60@3x.png", 55 | "scale" : "3x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "Icon-App-20x20@1x.png", 61 | "scale" : "1x" 62 | }, 63 | { 64 | "size" : "20x20", 65 | "idiom" : "ipad", 66 | "filename" : "Icon-App-20x20@2x.png", 67 | "scale" : "2x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-App-29x29@1x.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "29x29", 77 | "idiom" : "ipad", 78 | "filename" : "Icon-App-29x29@2x.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-App-40x40@1x.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "40x40", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-App-40x40@2x.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-App-76x76@1x.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "76x76", 101 | "idiom" : "ipad", 102 | "filename" : "Icon-App-76x76@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "83.5x83.5", 107 | "idiom" : "ipad", 108 | "filename" : "Icon-App-83.5x83.5@2x.png", 109 | "scale" : "2x" 110 | }, 111 | { 112 | "size" : "1024x1024", 113 | "idiom" : "ios-marketing", 114 | "filename" : "Icon-App-1024x1024@1x.png", 115 | "scale" : "1x" 116 | } 117 | ], 118 | "info" : { 119 | "version" : 1, 120 | "author" : "xcode" 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/torikatsupg/flutter_todo/a8286ca196bbb52ea4797d2273662e0665879af2/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/torikatsupg/flutter_todo/a8286ca196bbb52ea4797d2273662e0665879af2/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/torikatsupg/flutter_todo/a8286ca196bbb52ea4797d2273662e0665879af2/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/torikatsupg/flutter_todo/a8286ca196bbb52ea4797d2273662e0665879af2/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/torikatsupg/flutter_todo/a8286ca196bbb52ea4797d2273662e0665879af2/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/torikatsupg/flutter_todo/a8286ca196bbb52ea4797d2273662e0665879af2/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/torikatsupg/flutter_todo/a8286ca196bbb52ea4797d2273662e0665879af2/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/torikatsupg/flutter_todo/a8286ca196bbb52ea4797d2273662e0665879af2/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/torikatsupg/flutter_todo/a8286ca196bbb52ea4797d2273662e0665879af2/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/torikatsupg/flutter_todo/a8286ca196bbb52ea4797d2273662e0665879af2/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/torikatsupg/flutter_todo/a8286ca196bbb52ea4797d2273662e0665879af2/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/torikatsupg/flutter_todo/a8286ca196bbb52ea4797d2273662e0665879af2/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/torikatsupg/flutter_todo/a8286ca196bbb52ea4797d2273662e0665879af2/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/torikatsupg/flutter_todo/a8286ca196bbb52ea4797d2273662e0665879af2/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/torikatsupg/flutter_todo/a8286ca196bbb52ea4797d2273662e0665879af2/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "LaunchImage.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "LaunchImage@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "LaunchImage@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/torikatsupg/flutter_todo/a8286ca196bbb52ea4797d2273662e0665879af2/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/torikatsupg/flutter_todo/a8286ca196bbb52ea4797d2273662e0665879af2/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/torikatsupg/flutter_todo/a8286ca196bbb52ea4797d2273662e0665879af2/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md: -------------------------------------------------------------------------------- 1 | # Launch Screen Assets 2 | 3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory. 4 | 5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleDisplayName 8 | Flutter Todo 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | flutter_todo 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | $(FLUTTER_BUILD_NAME) 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | $(FLUTTER_BUILD_NUMBER) 25 | LSRequiresIPhoneOS 26 | 27 | UILaunchStoryboardName 28 | LaunchScreen 29 | UIMainStoryboardFile 30 | Main 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | UIViewControllerBasedStatusBarAppearance 45 | 46 | CADisableMinimumFrameDurationOnPhone 47 | 48 | 49 | FlutterDeepLinkingEnabled 50 | 51 | CFBundleURLTypes 52 | 53 | 54 | CFBundleTypeRole 55 | Editor 56 | CFBundleURLName 57 | dev.fluttertodo.com 58 | CFBundleURLSchemes 59 | 60 | fluttertodo 61 | 62 | 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /lib/app.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_todo/provider/infrastructure/user_provider.dart'; 3 | import 'package:flutter_todo/provider/route/router_provider.dart'; 4 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 5 | 6 | class App extends ConsumerWidget { 7 | const App({super.key}); 8 | 9 | @override 10 | Widget build(BuildContext context, WidgetRef ref) { 11 | final router = ref.watch(routerProvider); 12 | 13 | final a = "hoge"; 14 | return FutureBuilder( 15 | future: _init(ref), 16 | builder: (context, snapshot) { 17 | if (snapshot.hasData) { 18 | return MaterialApp.router( 19 | routeInformationProvider: router.routeInformationProvider, 20 | routeInformationParser: router.routeInformationParser, 21 | routerDelegate: router.routerDelegate, 22 | ); 23 | } else { 24 | // TODO(torikatsu): show splash screen 25 | return const Center(child: CircularProgressIndicator()); 26 | } 27 | }, 28 | ); 29 | } 30 | } 31 | 32 | /// initializing between [runApp] and [MaterialApp.router] 33 | Future _init(WidgetRef ref) async { 34 | await Future.wait([ 35 | // [FirebaseAuth.instance.authStateChange] sends User to the Stream 36 | // at initialization if authenticated, or null if not authenticated. 37 | // To avoid having the sign-in screen appear momentarily despite 38 | // the fact that the user has authenticated when the app is launched, 39 | // the GoRouter is attached after waiting for the first event. 40 | ref.read(userProvider.stream).first, 41 | ]); 42 | return true; 43 | } 44 | -------------------------------------------------------------------------------- /lib/config/page_storage_keys.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | enum PageStorageKies { 4 | todoTab("todo_tab"), 5 | doneTab("done_tab"); 6 | 7 | const PageStorageKies(this._key); 8 | final String _key; 9 | PageStorageKey get key => PageStorageKey(_key); 10 | } 11 | -------------------------------------------------------------------------------- /lib/firebase_options.dart: -------------------------------------------------------------------------------- 1 | // File generated by FlutterFire CLI. 2 | // ignore_for_file: lines_longer_than_80_chars 3 | import 'package:firebase_core/firebase_core.dart' show FirebaseOptions; 4 | import 'package:flutter/foundation.dart' 5 | show defaultTargetPlatform, kIsWeb, TargetPlatform; 6 | 7 | /// Default [FirebaseOptions] for use with your Firebase apps. 8 | /// 9 | /// Example: 10 | /// ```dart 11 | /// import 'firebase_options.dart'; 12 | /// // ... 13 | /// await Firebase.initializeApp( 14 | /// options: DefaultFirebaseOptions.currentPlatform, 15 | /// ); 16 | /// ``` 17 | class DefaultFirebaseOptions { 18 | static FirebaseOptions get currentPlatform { 19 | if (kIsWeb) { 20 | return web; 21 | } 22 | // ignore: missing_enum_constant_in_switch 23 | switch (defaultTargetPlatform) { 24 | case TargetPlatform.android: 25 | return android; 26 | case TargetPlatform.iOS: 27 | return ios; 28 | case TargetPlatform.macOS: 29 | throw UnsupportedError( 30 | 'DefaultFirebaseOptions have not been configured for macos - ' 31 | 'you can reconfigure this by running the FlutterFire CLI again.', 32 | ); 33 | } 34 | 35 | throw UnsupportedError( 36 | 'DefaultFirebaseOptions are not supported for this platform.', 37 | ); 38 | } 39 | 40 | static const FirebaseOptions web = FirebaseOptions( 41 | apiKey: 'AIzaSyCQr08fqo7ClZex1JHC0h3woY1pDsAvAtY', 42 | appId: '1:857958773511:web:1a783b50be5f73698b8ba2', 43 | messagingSenderId: '857958773511', 44 | projectId: 'flutter-architecture-11ca3', 45 | authDomain: 'flutter-architecture-11ca3.firebaseapp.com', 46 | storageBucket: 'flutter-architecture-11ca3.appspot.com', 47 | measurementId: 'G-2EMK9QSP5Z', 48 | ); 49 | 50 | static const FirebaseOptions android = FirebaseOptions( 51 | apiKey: 'AIzaSyAYDTRCnOD9nXWbjiwv86wIoMj-VELLuBU', 52 | appId: '1:857958773511:android:914ccbfb8bfa53278b8ba2', 53 | messagingSenderId: '857958773511', 54 | projectId: 'flutter-architecture-11ca3', 55 | storageBucket: 'flutter-architecture-11ca3.appspot.com', 56 | ); 57 | 58 | static const FirebaseOptions ios = FirebaseOptions( 59 | apiKey: 'AIzaSyAKLnnAbYou7besNm78QbUPKB8ZtBuSIlo', 60 | appId: '1:857958773511:ios:1475a87d112bd5ed8b8ba2', 61 | messagingSenderId: '857958773511', 62 | projectId: 'flutter-architecture-11ca3', 63 | storageBucket: 'flutter-architecture-11ca3.appspot.com', 64 | iosClientId: '857958773511-c78lvg536uacmqerq11g46i25p3virr1.apps.googleusercontent.com', 65 | iosBundleId: 'com.example.flutterTodo', 66 | ); 67 | } 68 | -------------------------------------------------------------------------------- /lib/generated/util/tupple.freezed.dart: -------------------------------------------------------------------------------- 1 | // coverage:ignore-file 2 | // GENERATED CODE - DO NOT MODIFY BY HAND 3 | // ignore_for_file: type=lint 4 | // ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target 5 | 6 | part of '../../util/tupple.dart'; 7 | 8 | // ************************************************************************** 9 | // FreezedGenerator 10 | // ************************************************************************** 11 | 12 | T _$identity(T value) => value; 13 | 14 | final _privateConstructorUsedError = UnsupportedError( 15 | 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods'); 16 | 17 | /// @nodoc 18 | mixin _$T2 { 19 | V1 get v1 => throw _privateConstructorUsedError; 20 | V2 get v2 => throw _privateConstructorUsedError; 21 | 22 | @JsonKey(ignore: true) 23 | $T2CopyWith> get copyWith => 24 | throw _privateConstructorUsedError; 25 | } 26 | 27 | /// @nodoc 28 | abstract class $T2CopyWith { 29 | factory $T2CopyWith(T2 value, $Res Function(T2) then) = 30 | _$T2CopyWithImpl; 31 | $Res call({V1 v1, V2 v2}); 32 | } 33 | 34 | /// @nodoc 35 | class _$T2CopyWithImpl implements $T2CopyWith { 36 | _$T2CopyWithImpl(this._value, this._then); 37 | 38 | final T2 _value; 39 | // ignore: unused_field 40 | final $Res Function(T2) _then; 41 | 42 | @override 43 | $Res call({ 44 | Object? v1 = freezed, 45 | Object? v2 = freezed, 46 | }) { 47 | return _then(_value.copyWith( 48 | v1: v1 == freezed 49 | ? _value.v1 50 | : v1 // ignore: cast_nullable_to_non_nullable 51 | as V1, 52 | v2: v2 == freezed 53 | ? _value.v2 54 | : v2 // ignore: cast_nullable_to_non_nullable 55 | as V2, 56 | )); 57 | } 58 | } 59 | 60 | /// @nodoc 61 | abstract class _$$_T2CopyWith 62 | implements $T2CopyWith { 63 | factory _$$_T2CopyWith( 64 | _$_T2 value, $Res Function(_$_T2) then) = 65 | __$$_T2CopyWithImpl; 66 | @override 67 | $Res call({V1 v1, V2 v2}); 68 | } 69 | 70 | /// @nodoc 71 | class __$$_T2CopyWithImpl extends _$T2CopyWithImpl 72 | implements _$$_T2CopyWith { 73 | __$$_T2CopyWithImpl(_$_T2 _value, $Res Function(_$_T2) _then) 74 | : super(_value, (v) => _then(v as _$_T2)); 75 | 76 | @override 77 | _$_T2 get _value => super._value as _$_T2; 78 | 79 | @override 80 | $Res call({ 81 | Object? v1 = freezed, 82 | Object? v2 = freezed, 83 | }) { 84 | return _then(_$_T2( 85 | v1 == freezed 86 | ? _value.v1 87 | : v1 // ignore: cast_nullable_to_non_nullable 88 | as V1, 89 | v2 == freezed 90 | ? _value.v2 91 | : v2 // ignore: cast_nullable_to_non_nullable 92 | as V2, 93 | )); 94 | } 95 | } 96 | 97 | /// @nodoc 98 | 99 | class _$_T2 extends _T2 { 100 | _$_T2(this.v1, this.v2) : super._(); 101 | 102 | @override 103 | final V1 v1; 104 | @override 105 | final V2 v2; 106 | 107 | @override 108 | String toString() { 109 | return 'T2<$V1, $V2>(v1: $v1, v2: $v2)'; 110 | } 111 | 112 | @override 113 | bool operator ==(dynamic other) { 114 | return identical(this, other) || 115 | (other.runtimeType == runtimeType && 116 | other is _$_T2 && 117 | const DeepCollectionEquality().equals(other.v1, v1) && 118 | const DeepCollectionEquality().equals(other.v2, v2)); 119 | } 120 | 121 | @override 122 | int get hashCode => Object.hash( 123 | runtimeType, 124 | const DeepCollectionEquality().hash(v1), 125 | const DeepCollectionEquality().hash(v2)); 126 | 127 | @JsonKey(ignore: true) 128 | @override 129 | _$$_T2CopyWith> get copyWith => 130 | __$$_T2CopyWithImpl>(this, _$identity); 131 | } 132 | 133 | abstract class _T2 extends T2 { 134 | factory _T2(final V1 v1, final V2 v2) = _$_T2; 135 | _T2._() : super._(); 136 | 137 | @override 138 | V1 get v1; 139 | @override 140 | V2 get v2; 141 | @override 142 | @JsonKey(ignore: true) 143 | _$$_T2CopyWith> get copyWith => 144 | throw _privateConstructorUsedError; 145 | } 146 | -------------------------------------------------------------------------------- /lib/generated/view/page/create_task_page/create_task_page_controller.freezed.dart: -------------------------------------------------------------------------------- 1 | // coverage:ignore-file 2 | // GENERATED CODE - DO NOT MODIFY BY HAND 3 | // ignore_for_file: type=lint 4 | // ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target 5 | 6 | part of '../../../../view/page/create_task_page/create_task_page_controller.dart'; 7 | 8 | // ************************************************************************** 9 | // FreezedGenerator 10 | // ************************************************************************** 11 | 12 | T _$identity(T value) => value; 13 | 14 | final _privateConstructorUsedError = UnsupportedError( 15 | 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods'); 16 | 17 | /// @nodoc 18 | mixin _$_CreateTaskState { 19 | FormModel get name => throw _privateConstructorUsedError; 20 | 21 | @JsonKey(ignore: true) 22 | _$CreateTaskStateCopyWith<_CreateTaskState> get copyWith => 23 | throw _privateConstructorUsedError; 24 | } 25 | 26 | /// @nodoc 27 | abstract class _$CreateTaskStateCopyWith<$Res> { 28 | factory _$CreateTaskStateCopyWith( 29 | _CreateTaskState value, $Res Function(_CreateTaskState) then) = 30 | __$CreateTaskStateCopyWithImpl<$Res>; 31 | $Res call({FormModel name}); 32 | 33 | $FormModelCopyWith<$Res> get name; 34 | } 35 | 36 | /// @nodoc 37 | class __$CreateTaskStateCopyWithImpl<$Res> 38 | implements _$CreateTaskStateCopyWith<$Res> { 39 | __$CreateTaskStateCopyWithImpl(this._value, this._then); 40 | 41 | final _CreateTaskState _value; 42 | // ignore: unused_field 43 | final $Res Function(_CreateTaskState) _then; 44 | 45 | @override 46 | $Res call({ 47 | Object? name = freezed, 48 | }) { 49 | return _then(_value.copyWith( 50 | name: name == freezed 51 | ? _value.name 52 | : name // ignore: cast_nullable_to_non_nullable 53 | as FormModel, 54 | )); 55 | } 56 | 57 | @override 58 | $FormModelCopyWith<$Res> get name { 59 | return $FormModelCopyWith<$Res>(_value.name, (value) { 60 | return _then(_value.copyWith(name: value)); 61 | }); 62 | } 63 | } 64 | 65 | /// @nodoc 66 | abstract class _$$__CreateTaskControllerCopyWith<$Res> 67 | implements _$CreateTaskStateCopyWith<$Res> { 68 | factory _$$__CreateTaskControllerCopyWith(_$__CreateTaskController value, 69 | $Res Function(_$__CreateTaskController) then) = 70 | __$$__CreateTaskControllerCopyWithImpl<$Res>; 71 | @override 72 | $Res call({FormModel name}); 73 | 74 | @override 75 | $FormModelCopyWith<$Res> get name; 76 | } 77 | 78 | /// @nodoc 79 | class __$$__CreateTaskControllerCopyWithImpl<$Res> 80 | extends __$CreateTaskStateCopyWithImpl<$Res> 81 | implements _$$__CreateTaskControllerCopyWith<$Res> { 82 | __$$__CreateTaskControllerCopyWithImpl(_$__CreateTaskController _value, 83 | $Res Function(_$__CreateTaskController) _then) 84 | : super(_value, (v) => _then(v as _$__CreateTaskController)); 85 | 86 | @override 87 | _$__CreateTaskController get _value => 88 | super._value as _$__CreateTaskController; 89 | 90 | @override 91 | $Res call({ 92 | Object? name = freezed, 93 | }) { 94 | return _then(_$__CreateTaskController( 95 | name: name == freezed 96 | ? _value.name 97 | : name // ignore: cast_nullable_to_non_nullable 98 | as FormModel, 99 | )); 100 | } 101 | } 102 | 103 | /// @nodoc 104 | 105 | class _$__CreateTaskController extends __CreateTaskController { 106 | _$__CreateTaskController({required this.name}) : super._(); 107 | 108 | @override 109 | final FormModel name; 110 | 111 | @override 112 | String toString() { 113 | return '_CreateTaskState(name: $name)'; 114 | } 115 | 116 | @override 117 | bool operator ==(dynamic other) { 118 | return identical(this, other) || 119 | (other.runtimeType == runtimeType && 120 | other is _$__CreateTaskController && 121 | const DeepCollectionEquality().equals(other.name, name)); 122 | } 123 | 124 | @override 125 | int get hashCode => 126 | Object.hash(runtimeType, const DeepCollectionEquality().hash(name)); 127 | 128 | @JsonKey(ignore: true) 129 | @override 130 | _$$__CreateTaskControllerCopyWith<_$__CreateTaskController> get copyWith => 131 | __$$__CreateTaskControllerCopyWithImpl<_$__CreateTaskController>( 132 | this, _$identity); 133 | } 134 | 135 | abstract class __CreateTaskController extends _CreateTaskState { 136 | factory __CreateTaskController({required final FormModel name}) = 137 | _$__CreateTaskController; 138 | __CreateTaskController._() : super._(); 139 | 140 | @override 141 | FormModel get name; 142 | @override 143 | @JsonKey(ignore: true) 144 | _$$__CreateTaskControllerCopyWith<_$__CreateTaskController> get copyWith => 145 | throw _privateConstructorUsedError; 146 | } 147 | -------------------------------------------------------------------------------- /lib/generated/view/page/edit_task_page/edit_task_page_controller.freezed.dart: -------------------------------------------------------------------------------- 1 | // coverage:ignore-file 2 | // GENERATED CODE - DO NOT MODIFY BY HAND 3 | // ignore_for_file: type=lint 4 | // ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target 5 | 6 | part of '../../../../view/page/edit_task_page/edit_task_page_controller.dart'; 7 | 8 | // ************************************************************************** 9 | // FreezedGenerator 10 | // ************************************************************************** 11 | 12 | T _$identity(T value) => value; 13 | 14 | final _privateConstructorUsedError = UnsupportedError( 15 | 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods'); 16 | 17 | /// @nodoc 18 | mixin _$_TaskEditState { 19 | Task get initTask => throw _privateConstructorUsedError; 20 | FormModel get name => throw _privateConstructorUsedError; 21 | 22 | @JsonKey(ignore: true) 23 | _$TaskEditStateCopyWith<_TaskEditState> get copyWith => 24 | throw _privateConstructorUsedError; 25 | } 26 | 27 | /// @nodoc 28 | abstract class _$TaskEditStateCopyWith<$Res> { 29 | factory _$TaskEditStateCopyWith( 30 | _TaskEditState value, $Res Function(_TaskEditState) then) = 31 | __$TaskEditStateCopyWithImpl<$Res>; 32 | $Res call({Task initTask, FormModel name}); 33 | 34 | $TaskCopyWith<$Res> get initTask; 35 | $FormModelCopyWith<$Res> get name; 36 | } 37 | 38 | /// @nodoc 39 | class __$TaskEditStateCopyWithImpl<$Res> 40 | implements _$TaskEditStateCopyWith<$Res> { 41 | __$TaskEditStateCopyWithImpl(this._value, this._then); 42 | 43 | final _TaskEditState _value; 44 | // ignore: unused_field 45 | final $Res Function(_TaskEditState) _then; 46 | 47 | @override 48 | $Res call({ 49 | Object? initTask = freezed, 50 | Object? name = freezed, 51 | }) { 52 | return _then(_value.copyWith( 53 | initTask: initTask == freezed 54 | ? _value.initTask 55 | : initTask // ignore: cast_nullable_to_non_nullable 56 | as Task, 57 | name: name == freezed 58 | ? _value.name 59 | : name // ignore: cast_nullable_to_non_nullable 60 | as FormModel, 61 | )); 62 | } 63 | 64 | @override 65 | $TaskCopyWith<$Res> get initTask { 66 | return $TaskCopyWith<$Res>(_value.initTask, (value) { 67 | return _then(_value.copyWith(initTask: value)); 68 | }); 69 | } 70 | 71 | @override 72 | $FormModelCopyWith<$Res> get name { 73 | return $FormModelCopyWith<$Res>(_value.name, (value) { 74 | return _then(_value.copyWith(name: value)); 75 | }); 76 | } 77 | } 78 | 79 | /// @nodoc 80 | abstract class _$$__TaskEditStateCopyWith<$Res> 81 | implements _$TaskEditStateCopyWith<$Res> { 82 | factory _$$__TaskEditStateCopyWith( 83 | _$__TaskEditState value, $Res Function(_$__TaskEditState) then) = 84 | __$$__TaskEditStateCopyWithImpl<$Res>; 85 | @override 86 | $Res call({Task initTask, FormModel name}); 87 | 88 | @override 89 | $TaskCopyWith<$Res> get initTask; 90 | @override 91 | $FormModelCopyWith<$Res> get name; 92 | } 93 | 94 | /// @nodoc 95 | class __$$__TaskEditStateCopyWithImpl<$Res> 96 | extends __$TaskEditStateCopyWithImpl<$Res> 97 | implements _$$__TaskEditStateCopyWith<$Res> { 98 | __$$__TaskEditStateCopyWithImpl( 99 | _$__TaskEditState _value, $Res Function(_$__TaskEditState) _then) 100 | : super(_value, (v) => _then(v as _$__TaskEditState)); 101 | 102 | @override 103 | _$__TaskEditState get _value => super._value as _$__TaskEditState; 104 | 105 | @override 106 | $Res call({ 107 | Object? initTask = freezed, 108 | Object? name = freezed, 109 | }) { 110 | return _then(_$__TaskEditState( 111 | initTask: initTask == freezed 112 | ? _value.initTask 113 | : initTask // ignore: cast_nullable_to_non_nullable 114 | as Task, 115 | name: name == freezed 116 | ? _value.name 117 | : name // ignore: cast_nullable_to_non_nullable 118 | as FormModel, 119 | )); 120 | } 121 | } 122 | 123 | /// @nodoc 124 | 125 | class _$__TaskEditState extends __TaskEditState { 126 | _$__TaskEditState({required this.initTask, required this.name}) : super._(); 127 | 128 | @override 129 | final Task initTask; 130 | @override 131 | final FormModel name; 132 | 133 | @override 134 | String toString() { 135 | return '_TaskEditState(initTask: $initTask, name: $name)'; 136 | } 137 | 138 | @override 139 | bool operator ==(dynamic other) { 140 | return identical(this, other) || 141 | (other.runtimeType == runtimeType && 142 | other is _$__TaskEditState && 143 | const DeepCollectionEquality().equals(other.initTask, initTask) && 144 | const DeepCollectionEquality().equals(other.name, name)); 145 | } 146 | 147 | @override 148 | int get hashCode => Object.hash( 149 | runtimeType, 150 | const DeepCollectionEquality().hash(initTask), 151 | const DeepCollectionEquality().hash(name)); 152 | 153 | @JsonKey(ignore: true) 154 | @override 155 | _$$__TaskEditStateCopyWith<_$__TaskEditState> get copyWith => 156 | __$$__TaskEditStateCopyWithImpl<_$__TaskEditState>(this, _$identity); 157 | } 158 | 159 | abstract class __TaskEditState extends _TaskEditState { 160 | factory __TaskEditState( 161 | {required final Task initTask, 162 | required final FormModel name}) = _$__TaskEditState; 163 | __TaskEditState._() : super._(); 164 | 165 | @override 166 | Task get initTask; 167 | @override 168 | FormModel get name; 169 | @override 170 | @JsonKey(ignore: true) 171 | _$$__TaskEditStateCopyWith<_$__TaskEditState> get copyWith => 172 | throw _privateConstructorUsedError; 173 | } 174 | -------------------------------------------------------------------------------- /lib/generated/view/page/register_page/register_page_controller.freezed.dart: -------------------------------------------------------------------------------- 1 | // coverage:ignore-file 2 | // GENERATED CODE - DO NOT MODIFY BY HAND 3 | // ignore_for_file: type=lint 4 | // ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target 5 | 6 | part of '../../../../view/page/register_page/register_page_controller.dart'; 7 | 8 | // ************************************************************************** 9 | // FreezedGenerator 10 | // ************************************************************************** 11 | 12 | T _$identity(T value) => value; 13 | 14 | final _privateConstructorUsedError = UnsupportedError( 15 | 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods'); 16 | 17 | /// @nodoc 18 | mixin _$RegisterPageState { 19 | FormModel get username => throw _privateConstructorUsedError; 20 | 21 | @JsonKey(ignore: true) 22 | $RegisterPageStateCopyWith get copyWith => 23 | throw _privateConstructorUsedError; 24 | } 25 | 26 | /// @nodoc 27 | abstract class $RegisterPageStateCopyWith<$Res> { 28 | factory $RegisterPageStateCopyWith( 29 | RegisterPageState value, $Res Function(RegisterPageState) then) = 30 | _$RegisterPageStateCopyWithImpl<$Res>; 31 | $Res call({FormModel username}); 32 | 33 | $FormModelCopyWith<$Res> get username; 34 | } 35 | 36 | /// @nodoc 37 | class _$RegisterPageStateCopyWithImpl<$Res> 38 | implements $RegisterPageStateCopyWith<$Res> { 39 | _$RegisterPageStateCopyWithImpl(this._value, this._then); 40 | 41 | final RegisterPageState _value; 42 | // ignore: unused_field 43 | final $Res Function(RegisterPageState) _then; 44 | 45 | @override 46 | $Res call({ 47 | Object? username = freezed, 48 | }) { 49 | return _then(_value.copyWith( 50 | username: username == freezed 51 | ? _value.username 52 | : username // ignore: cast_nullable_to_non_nullable 53 | as FormModel, 54 | )); 55 | } 56 | 57 | @override 58 | $FormModelCopyWith<$Res> get username { 59 | return $FormModelCopyWith<$Res>(_value.username, (value) { 60 | return _then(_value.copyWith(username: value)); 61 | }); 62 | } 63 | } 64 | 65 | /// @nodoc 66 | abstract class _$$_RegisterPageStateCopyWith<$Res> 67 | implements $RegisterPageStateCopyWith<$Res> { 68 | factory _$$_RegisterPageStateCopyWith(_$_RegisterPageState value, 69 | $Res Function(_$_RegisterPageState) then) = 70 | __$$_RegisterPageStateCopyWithImpl<$Res>; 71 | @override 72 | $Res call({FormModel username}); 73 | 74 | @override 75 | $FormModelCopyWith<$Res> get username; 76 | } 77 | 78 | /// @nodoc 79 | class __$$_RegisterPageStateCopyWithImpl<$Res> 80 | extends _$RegisterPageStateCopyWithImpl<$Res> 81 | implements _$$_RegisterPageStateCopyWith<$Res> { 82 | __$$_RegisterPageStateCopyWithImpl( 83 | _$_RegisterPageState _value, $Res Function(_$_RegisterPageState) _then) 84 | : super(_value, (v) => _then(v as _$_RegisterPageState)); 85 | 86 | @override 87 | _$_RegisterPageState get _value => super._value as _$_RegisterPageState; 88 | 89 | @override 90 | $Res call({ 91 | Object? username = freezed, 92 | }) { 93 | return _then(_$_RegisterPageState( 94 | username: username == freezed 95 | ? _value.username 96 | : username // ignore: cast_nullable_to_non_nullable 97 | as FormModel, 98 | )); 99 | } 100 | } 101 | 102 | /// @nodoc 103 | 104 | class _$_RegisterPageState extends _RegisterPageState { 105 | _$_RegisterPageState({required this.username}) : super._(); 106 | 107 | @override 108 | final FormModel username; 109 | 110 | @override 111 | String toString() { 112 | return 'RegisterPageState(username: $username)'; 113 | } 114 | 115 | @override 116 | bool operator ==(dynamic other) { 117 | return identical(this, other) || 118 | (other.runtimeType == runtimeType && 119 | other is _$_RegisterPageState && 120 | const DeepCollectionEquality().equals(other.username, username)); 121 | } 122 | 123 | @override 124 | int get hashCode => 125 | Object.hash(runtimeType, const DeepCollectionEquality().hash(username)); 126 | 127 | @JsonKey(ignore: true) 128 | @override 129 | _$$_RegisterPageStateCopyWith<_$_RegisterPageState> get copyWith => 130 | __$$_RegisterPageStateCopyWithImpl<_$_RegisterPageState>( 131 | this, _$identity); 132 | } 133 | 134 | abstract class _RegisterPageState extends RegisterPageState { 135 | factory _RegisterPageState({required final FormModel username}) = 136 | _$_RegisterPageState; 137 | _RegisterPageState._() : super._(); 138 | 139 | @override 140 | FormModel get username; 141 | @override 142 | @JsonKey(ignore: true) 143 | _$$_RegisterPageStateCopyWith<_$_RegisterPageState> get copyWith => 144 | throw _privateConstructorUsedError; 145 | } 146 | -------------------------------------------------------------------------------- /lib/generated/view/page/setting_page/setting_page_controller.freezed.dart: -------------------------------------------------------------------------------- 1 | // coverage:ignore-file 2 | // GENERATED CODE - DO NOT MODIFY BY HAND 3 | // ignore_for_file: type=lint 4 | // ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target 5 | 6 | part of '../../../../view/page/setting_page/setting_page_controller.dart'; 7 | 8 | // ************************************************************************** 9 | // FreezedGenerator 10 | // ************************************************************************** 11 | 12 | T _$identity(T value) => value; 13 | 14 | final _privateConstructorUsedError = UnsupportedError( 15 | 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods'); 16 | 17 | /// @nodoc 18 | mixin _$SettingPageState { 19 | User get initUser => throw _privateConstructorUsedError; 20 | FormModel get username => throw _privateConstructorUsedError; 21 | 22 | @JsonKey(ignore: true) 23 | $SettingPageStateCopyWith get copyWith => 24 | throw _privateConstructorUsedError; 25 | } 26 | 27 | /// @nodoc 28 | abstract class $SettingPageStateCopyWith<$Res> { 29 | factory $SettingPageStateCopyWith( 30 | SettingPageState value, $Res Function(SettingPageState) then) = 31 | _$SettingPageStateCopyWithImpl<$Res>; 32 | $Res call({User initUser, FormModel username}); 33 | 34 | $UserCopyWith<$Res> get initUser; 35 | $FormModelCopyWith<$Res> get username; 36 | } 37 | 38 | /// @nodoc 39 | class _$SettingPageStateCopyWithImpl<$Res> 40 | implements $SettingPageStateCopyWith<$Res> { 41 | _$SettingPageStateCopyWithImpl(this._value, this._then); 42 | 43 | final SettingPageState _value; 44 | // ignore: unused_field 45 | final $Res Function(SettingPageState) _then; 46 | 47 | @override 48 | $Res call({ 49 | Object? initUser = freezed, 50 | Object? username = freezed, 51 | }) { 52 | return _then(_value.copyWith( 53 | initUser: initUser == freezed 54 | ? _value.initUser 55 | : initUser // ignore: cast_nullable_to_non_nullable 56 | as User, 57 | username: username == freezed 58 | ? _value.username 59 | : username // ignore: cast_nullable_to_non_nullable 60 | as FormModel, 61 | )); 62 | } 63 | 64 | @override 65 | $UserCopyWith<$Res> get initUser { 66 | return $UserCopyWith<$Res>(_value.initUser, (value) { 67 | return _then(_value.copyWith(initUser: value)); 68 | }); 69 | } 70 | 71 | @override 72 | $FormModelCopyWith<$Res> get username { 73 | return $FormModelCopyWith<$Res>(_value.username, (value) { 74 | return _then(_value.copyWith(username: value)); 75 | }); 76 | } 77 | } 78 | 79 | /// @nodoc 80 | abstract class _$$_SettingPageStateCopyWith<$Res> 81 | implements $SettingPageStateCopyWith<$Res> { 82 | factory _$$_SettingPageStateCopyWith( 83 | _$_SettingPageState value, $Res Function(_$_SettingPageState) then) = 84 | __$$_SettingPageStateCopyWithImpl<$Res>; 85 | @override 86 | $Res call({User initUser, FormModel username}); 87 | 88 | @override 89 | $UserCopyWith<$Res> get initUser; 90 | @override 91 | $FormModelCopyWith<$Res> get username; 92 | } 93 | 94 | /// @nodoc 95 | class __$$_SettingPageStateCopyWithImpl<$Res> 96 | extends _$SettingPageStateCopyWithImpl<$Res> 97 | implements _$$_SettingPageStateCopyWith<$Res> { 98 | __$$_SettingPageStateCopyWithImpl( 99 | _$_SettingPageState _value, $Res Function(_$_SettingPageState) _then) 100 | : super(_value, (v) => _then(v as _$_SettingPageState)); 101 | 102 | @override 103 | _$_SettingPageState get _value => super._value as _$_SettingPageState; 104 | 105 | @override 106 | $Res call({ 107 | Object? initUser = freezed, 108 | Object? username = freezed, 109 | }) { 110 | return _then(_$_SettingPageState( 111 | initUser: initUser == freezed 112 | ? _value.initUser 113 | : initUser // ignore: cast_nullable_to_non_nullable 114 | as User, 115 | username: username == freezed 116 | ? _value.username 117 | : username // ignore: cast_nullable_to_non_nullable 118 | as FormModel, 119 | )); 120 | } 121 | } 122 | 123 | /// @nodoc 124 | 125 | class _$_SettingPageState extends _SettingPageState { 126 | _$_SettingPageState({required this.initUser, required this.username}) 127 | : super._(); 128 | 129 | @override 130 | final User initUser; 131 | @override 132 | final FormModel username; 133 | 134 | @override 135 | String toString() { 136 | return 'SettingPageState(initUser: $initUser, username: $username)'; 137 | } 138 | 139 | @override 140 | bool operator ==(dynamic other) { 141 | return identical(this, other) || 142 | (other.runtimeType == runtimeType && 143 | other is _$_SettingPageState && 144 | const DeepCollectionEquality().equals(other.initUser, initUser) && 145 | const DeepCollectionEquality().equals(other.username, username)); 146 | } 147 | 148 | @override 149 | int get hashCode => Object.hash( 150 | runtimeType, 151 | const DeepCollectionEquality().hash(initUser), 152 | const DeepCollectionEquality().hash(username)); 153 | 154 | @JsonKey(ignore: true) 155 | @override 156 | _$$_SettingPageStateCopyWith<_$_SettingPageState> get copyWith => 157 | __$$_SettingPageStateCopyWithImpl<_$_SettingPageState>(this, _$identity); 158 | } 159 | 160 | abstract class _SettingPageState extends SettingPageState { 161 | factory _SettingPageState( 162 | {required final User initUser, 163 | required final FormModel username}) = _$_SettingPageState; 164 | _SettingPageState._() : super._(); 165 | 166 | @override 167 | User get initUser; 168 | @override 169 | FormModel get username; 170 | @override 171 | @JsonKey(ignore: true) 172 | _$$_SettingPageStateCopyWith<_$_SettingPageState> get copyWith => 173 | throw _privateConstructorUsedError; 174 | } 175 | -------------------------------------------------------------------------------- /lib/generated/view/page/signin_page/signin_page_controller.freezed.dart: -------------------------------------------------------------------------------- 1 | // coverage:ignore-file 2 | // GENERATED CODE - DO NOT MODIFY BY HAND 3 | // ignore_for_file: type=lint 4 | // ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target 5 | 6 | part of '../../../../view/page/signin_page/signin_page_controller.dart'; 7 | 8 | // ************************************************************************** 9 | // FreezedGenerator 10 | // ************************************************************************** 11 | 12 | T _$identity(T value) => value; 13 | 14 | final _privateConstructorUsedError = UnsupportedError( 15 | 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods'); 16 | 17 | /// @nodoc 18 | mixin _$_SigninState { 19 | FormModel get email => throw _privateConstructorUsedError; 20 | FormModel get password => throw _privateConstructorUsedError; 21 | 22 | @JsonKey(ignore: true) 23 | _$SigninStateCopyWith<_SigninState> get copyWith => 24 | throw _privateConstructorUsedError; 25 | } 26 | 27 | /// @nodoc 28 | abstract class _$SigninStateCopyWith<$Res> { 29 | factory _$SigninStateCopyWith( 30 | _SigninState value, $Res Function(_SigninState) then) = 31 | __$SigninStateCopyWithImpl<$Res>; 32 | $Res call({FormModel email, FormModel password}); 33 | 34 | $FormModelCopyWith<$Res> get email; 35 | $FormModelCopyWith<$Res> get password; 36 | } 37 | 38 | /// @nodoc 39 | class __$SigninStateCopyWithImpl<$Res> implements _$SigninStateCopyWith<$Res> { 40 | __$SigninStateCopyWithImpl(this._value, this._then); 41 | 42 | final _SigninState _value; 43 | // ignore: unused_field 44 | final $Res Function(_SigninState) _then; 45 | 46 | @override 47 | $Res call({ 48 | Object? email = freezed, 49 | Object? password = freezed, 50 | }) { 51 | return _then(_value.copyWith( 52 | email: email == freezed 53 | ? _value.email 54 | : email // ignore: cast_nullable_to_non_nullable 55 | as FormModel, 56 | password: password == freezed 57 | ? _value.password 58 | : password // ignore: cast_nullable_to_non_nullable 59 | as FormModel, 60 | )); 61 | } 62 | 63 | @override 64 | $FormModelCopyWith<$Res> get email { 65 | return $FormModelCopyWith<$Res>(_value.email, (value) { 66 | return _then(_value.copyWith(email: value)); 67 | }); 68 | } 69 | 70 | @override 71 | $FormModelCopyWith<$Res> get password { 72 | return $FormModelCopyWith<$Res>(_value.password, (value) { 73 | return _then(_value.copyWith(password: value)); 74 | }); 75 | } 76 | } 77 | 78 | /// @nodoc 79 | abstract class _$$__SigninInputStateCopyWith<$Res> 80 | implements _$SigninStateCopyWith<$Res> { 81 | factory _$$__SigninInputStateCopyWith(_$__SigninInputState value, 82 | $Res Function(_$__SigninInputState) then) = 83 | __$$__SigninInputStateCopyWithImpl<$Res>; 84 | @override 85 | $Res call({FormModel email, FormModel password}); 86 | 87 | @override 88 | $FormModelCopyWith<$Res> get email; 89 | @override 90 | $FormModelCopyWith<$Res> get password; 91 | } 92 | 93 | /// @nodoc 94 | class __$$__SigninInputStateCopyWithImpl<$Res> 95 | extends __$SigninStateCopyWithImpl<$Res> 96 | implements _$$__SigninInputStateCopyWith<$Res> { 97 | __$$__SigninInputStateCopyWithImpl( 98 | _$__SigninInputState _value, $Res Function(_$__SigninInputState) _then) 99 | : super(_value, (v) => _then(v as _$__SigninInputState)); 100 | 101 | @override 102 | _$__SigninInputState get _value => super._value as _$__SigninInputState; 103 | 104 | @override 105 | $Res call({ 106 | Object? email = freezed, 107 | Object? password = freezed, 108 | }) { 109 | return _then(_$__SigninInputState( 110 | email: email == freezed 111 | ? _value.email 112 | : email // ignore: cast_nullable_to_non_nullable 113 | as FormModel, 114 | password: password == freezed 115 | ? _value.password 116 | : password // ignore: cast_nullable_to_non_nullable 117 | as FormModel, 118 | )); 119 | } 120 | } 121 | 122 | /// @nodoc 123 | 124 | class _$__SigninInputState extends __SigninInputState { 125 | _$__SigninInputState({required this.email, required this.password}) 126 | : super._(); 127 | 128 | @override 129 | final FormModel email; 130 | @override 131 | final FormModel password; 132 | 133 | @override 134 | String toString() { 135 | return '_SigninState(email: $email, password: $password)'; 136 | } 137 | 138 | @override 139 | bool operator ==(dynamic other) { 140 | return identical(this, other) || 141 | (other.runtimeType == runtimeType && 142 | other is _$__SigninInputState && 143 | const DeepCollectionEquality().equals(other.email, email) && 144 | const DeepCollectionEquality().equals(other.password, password)); 145 | } 146 | 147 | @override 148 | int get hashCode => Object.hash( 149 | runtimeType, 150 | const DeepCollectionEquality().hash(email), 151 | const DeepCollectionEquality().hash(password)); 152 | 153 | @JsonKey(ignore: true) 154 | @override 155 | _$$__SigninInputStateCopyWith<_$__SigninInputState> get copyWith => 156 | __$$__SigninInputStateCopyWithImpl<_$__SigninInputState>( 157 | this, _$identity); 158 | } 159 | 160 | abstract class __SigninInputState extends _SigninState { 161 | factory __SigninInputState( 162 | {required final FormModel email, 163 | required final FormModel password}) = _$__SigninInputState; 164 | __SigninInputState._() : super._(); 165 | 166 | @override 167 | FormModel get email; 168 | @override 169 | FormModel get password; 170 | @override 171 | @JsonKey(ignore: true) 172 | _$$__SigninInputStateCopyWith<_$__SigninInputState> get copyWith => 173 | throw _privateConstructorUsedError; 174 | } 175 | -------------------------------------------------------------------------------- /lib/infrastructure/authenticator_provider.dart: -------------------------------------------------------------------------------- 1 | import 'package:firebase_auth/firebase_auth.dart'; 2 | import 'package:flutter_todo/model/result.dart'; 3 | import 'package:flutter_todo/provider/infrastructure/user_provider.dart'; 4 | import 'package:flutter_todo/util/logger.dart'; 5 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 6 | 7 | final authenticatorProvider = Provider(Authenticator.new); 8 | 9 | class Authenticator { 10 | Authenticator(this._ref); 11 | final Ref _ref; 12 | 13 | Future> signup({ 14 | required String email, 15 | required String password, 16 | }) async { 17 | try { 18 | await FirebaseAuth.instance 19 | .createUserWithEmailAndPassword(email: email, password: password); 20 | // wait for update of user 21 | await _ref.read(userProvider.stream).first; 22 | return Result.ok(FirebaseAuth.instance.currentUser); 23 | } on FirebaseAuthException catch (e, sc) { 24 | switch (e.code) { 25 | case 'email-already-in-use': 26 | return Result.err(SignupError.emailAlreadyInUse); 27 | case 'network-request-failed': 28 | return Result.err(SignupError.network); 29 | case 'invalid-email': 30 | logError(e, sc); 31 | rethrow; 32 | case 'operation-not-allowed': 33 | logError(e, sc); 34 | rethrow; 35 | case 'weak-password': 36 | logError(e, sc); 37 | rethrow; 38 | default: 39 | logError(e, sc); 40 | rethrow; 41 | } 42 | } catch (e, sc) { 43 | logError(e, sc); 44 | rethrow; 45 | } 46 | } 47 | 48 | Future> signin({ 49 | required String email, 50 | required String password, 51 | }) async { 52 | try { 53 | await FirebaseAuth.instance 54 | .signInWithEmailAndPassword(email: email, password: password); 55 | // wait for update of user 56 | await _ref.read(userProvider.stream).first; 57 | return Result.ok(FirebaseAuth.instance.currentUser); 58 | } on FirebaseAuthException catch (e, sc) { 59 | switch (e.code) { 60 | case 'user-disabled': 61 | return Result.err(SigninError.userDisabled); 62 | case 'user-not-found': 63 | return Result.err(SigninError.userNotFound); 64 | case 'wrong-password': 65 | return Result.err(SigninError.wrongPassword); 66 | case 'network-request-failed': 67 | return Result.err(SigninError.network); 68 | case 'invalid-email': 69 | logError(e, sc); 70 | rethrow; 71 | default: 72 | logError(e, sc); 73 | rethrow; 74 | } 75 | } catch (e, sc) { 76 | logError(e, sc); 77 | rethrow; 78 | } 79 | } 80 | 81 | Future> signout() async { 82 | try { 83 | await FirebaseAuth.instance.signOut(); 84 | // wait for update of user 85 | await _ref.read(userProvider.stream).first; 86 | return Result.ok(null); 87 | } on FirebaseAuthException catch (e, sc) { 88 | switch (e.code) { 89 | case 'network-request-failed': 90 | return Result.err(SignOutError.network); 91 | default: 92 | logError(e, sc); 93 | rethrow; 94 | } 95 | } catch (e, sc) { 96 | logError(e, sc); 97 | rethrow; 98 | } 99 | } 100 | } 101 | 102 | enum SignupError { 103 | emailAlreadyInUse, 104 | network, 105 | } 106 | 107 | enum SigninError { 108 | wrongPassword, 109 | userDisabled, 110 | userNotFound, 111 | network, 112 | } 113 | 114 | enum SignOutError { network } 115 | -------------------------------------------------------------------------------- /lib/infrastructure/cursor_impl.dart: -------------------------------------------------------------------------------- 1 | import 'package:cloud_firestore/cloud_firestore.dart'; 2 | import 'package:flutter_todo/model/query_list.dart'; 3 | 4 | class CursorImpl extends Cursor { 5 | CursorImpl(this.value); 6 | DocumentSnapshot? value; 7 | } -------------------------------------------------------------------------------- /lib/infrastructure/firebase.dart: -------------------------------------------------------------------------------- 1 | import 'package:cloud_firestore/cloud_firestore.dart'; 2 | import 'package:firebase_auth/firebase_auth.dart'; 3 | import 'package:flutter_todo/firebase_options.dart'; 4 | import 'package:firebase_core/firebase_core.dart'; 5 | import 'package:flutter_todo/util/env.dart'; 6 | import 'package:flutter_todo/util/logger.dart'; 7 | 8 | const _localhost = 'localhost'; 9 | const firestorePort = 8080; 10 | const authPort = 9099; 11 | 12 | Future initializeFirebase() async { 13 | if (_isInitialized()) return; 14 | await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform); 15 | // await FirebaseAuth.instance.setPersistence(Persistence.LOCAL); 16 | 17 | if (Env.isEmulator) { 18 | FirebaseFirestore.instance.useFirestoreEmulator(_localhost, firestorePort); 19 | await Future.wait( 20 | [FirebaseAuth.instance.useAuthEmulator(_localhost, authPort)], 21 | ); 22 | } 23 | } 24 | 25 | bool _isInitialized() { 26 | try { 27 | return Firebase.apps.isNotEmpty; 28 | } on dynamic catch (e, sc) { 29 | logError(e, sc); 30 | return false; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /lib/infrastructure/firestore_error.dart: -------------------------------------------------------------------------------- 1 | enum FirestoreError { 2 | error, 3 | notFound; 4 | 5 | T maybeMap({ 6 | T Function()? error, 7 | T Function()? notFound, 8 | T Function()? orElse, 9 | }) { 10 | assert((error == null && notFound == null) && orElse != null); 11 | 12 | switch (this) { 13 | case FirestoreError.error: 14 | return (error ?? orElse!)(); 15 | case FirestoreError.notFound: 16 | return (notFound ?? orElse!)(); 17 | default: 18 | return orElse!(); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /lib/infrastructure/task_repository_impl.dart: -------------------------------------------------------------------------------- 1 | import 'package:cloud_firestore/cloud_firestore.dart'; 2 | import 'package:flutter_todo/infrastructure/cursor_impl.dart'; 3 | import 'package:flutter_todo/infrastructure/firestore_error.dart'; 4 | import 'package:flutter_todo/model/query_list.dart'; 5 | import 'package:flutter_todo/model/result.dart'; 6 | 7 | import 'package:flutter_todo/model/task.dart'; 8 | import 'package:flutter_todo/model/user.dart'; 9 | import 'package:flutter_todo/util/logger.dart'; 10 | 11 | const _name = 'name'; 12 | const _createdAt = 'createdAt'; 13 | const _isDone = 'isDone'; 14 | const limit = 20; 15 | 16 | class TaskRepositoryImpl implements TaskRepository { 17 | TaskRepositoryImpl(this.userId); 18 | 19 | final UserId userId; 20 | 21 | CollectionReference get _ref => FirebaseFirestore.instance 22 | .collection('users') 23 | .doc(userId.value) 24 | .collection('tasks'); 25 | 26 | @override 27 | Future, FirestoreError>> findAllTodo( 28 | [CursorImpl? cursor]) async { 29 | final query = _ref 30 | .where(_isDone, isEqualTo: false) 31 | .orderBy(_createdAt, descending: true) 32 | .limit(limit); 33 | final cursorDoc = cursor?.value; 34 | try { 35 | final result = await (cursorDoc == null 36 | ? query 37 | : query.startAfterDocument(cursorDoc)) 38 | .get(); 39 | 40 | final items = result.docs.map(_toTask).toList(); 41 | return Result.ok( 42 | QueryList( 43 | items, 44 | CursorImpl(result.docs.isEmpty ? null : result.docs.last), 45 | items.length == limit, 46 | ), 47 | ); 48 | } on dynamic catch (e, sc) { 49 | // TODO(torikatsu): handle firestore error. 50 | logError(e, sc); 51 | return Result.err(FirestoreError.error); 52 | } 53 | } 54 | 55 | @override 56 | Future, FirestoreError>> findAllDone( 57 | [CursorImpl? cursor]) async { 58 | final query = _ref 59 | .where(_isDone, isEqualTo: true) 60 | .orderBy(_createdAt, descending: true) 61 | .limit(limit); 62 | final cursorDoc = cursor?.value; 63 | try { 64 | final result = await (cursorDoc == null 65 | ? query 66 | : query.startAfterDocument(cursorDoc)) 67 | .get(); 68 | 69 | final items = result.docs.map(_toTask).toList(); 70 | return Result.ok( 71 | QueryList( 72 | items, 73 | CursorImpl(result.docs.isEmpty ? null : result.docs.last), 74 | items.length == limit, 75 | ), 76 | ); 77 | } on dynamic catch (e, sc) { 78 | // TODO(torikatsu): handle firestore error. 79 | logError(e, sc); 80 | return Result.err(FirestoreError.error); 81 | } 82 | } 83 | 84 | @override 85 | Future> findById(TaskId id) async { 86 | try { 87 | final result = await _ref.doc(id.value).get(); 88 | final data = result.data() as Map?; 89 | if (data == null) { 90 | return Result.err(FirestoreError.notFound); 91 | } else { 92 | return Result.ok( 93 | Task.restore( 94 | id: result.id, 95 | name: data[_name], 96 | createdAt: (data[_createdAt] as Timestamp).toDate(), 97 | isDone: data[_isDone], 98 | ), 99 | ); 100 | } 101 | } on dynamic catch (e, sc) { 102 | // TODO(torikatsu): handle firestore error. 103 | logError(e, sc); 104 | return Result.err(FirestoreError.error); 105 | } 106 | } 107 | 108 | @override 109 | Future> insert({required String name}) async { 110 | final id = _ref.doc().id; 111 | try { 112 | await _ref.doc(id).set( 113 | { 114 | _name: name, 115 | _createdAt: Timestamp.now(), 116 | _isDone: false, 117 | }, 118 | SetOptions(merge: false), 119 | ); 120 | final result = await _ref.doc(id).get(); 121 | return Result.ok(_toTask(result)); 122 | } on dynamic catch (e, sc) { 123 | // TODO(torikatsu): handle firestore error. 124 | logError(e, sc); 125 | return Result.err(FirestoreError.error); 126 | } 127 | } 128 | 129 | @override 130 | Future> update(Task task) async { 131 | try { 132 | await _ref.doc(task.id.value).update( 133 | { 134 | _name: task.name, 135 | _isDone: task.isDone, 136 | }, 137 | ); 138 | return Result.ok(null); 139 | } on dynamic catch (e, sc) { 140 | // TODO(torikatsu): handle firestore error. 141 | logError(e, sc); 142 | return Result.err(FirestoreError.error); 143 | } 144 | } 145 | 146 | Task _toTask(DocumentSnapshot doc) { 147 | final data = doc.data() as Map; 148 | return Task.restore( 149 | id: doc.id, 150 | name: data[_name], 151 | createdAt: (data[_createdAt] as Timestamp).toDate(), 152 | isDone: data[_isDone], 153 | ); 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /lib/infrastructure/user_repository_impl.dart: -------------------------------------------------------------------------------- 1 | import 'package:cloud_firestore/cloud_firestore.dart'; 2 | import 'package:flutter_todo/infrastructure/firestore_error.dart'; 3 | import 'package:flutter_todo/model/result.dart'; 4 | import 'package:flutter_todo/model/user.dart'; 5 | import 'package:flutter_todo/util/logger.dart'; 6 | 7 | const _username = 'username'; 8 | 9 | class UserRepositoryImpl extends UserRepository { 10 | @override 11 | Future> findById(UserId id) async { 12 | try { 13 | final result = await FirebaseFirestore.instance 14 | .collection('users') 15 | .doc(id.value) 16 | .get(); 17 | 18 | final data = result.data(); 19 | if (data == null) { 20 | return Result.err(FirestoreError.notFound); 21 | } else { 22 | return Result.ok( 23 | User( 24 | userId: id, 25 | username: data[_username], 26 | ), 27 | ); 28 | } 29 | } on dynamic catch (e, sc) { 30 | // TODO(torikatsu): handle error correctly 31 | logError(e, sc); 32 | return Result.err(FirestoreError.error); 33 | } 34 | } 35 | 36 | @override 37 | Future> register({ 38 | required UserId id, 39 | required String username, 40 | }) async { 41 | try { 42 | await FirebaseFirestore.instance.collection('users').doc(id.value).set( 43 | { 44 | _username: username, 45 | }, 46 | ); 47 | return Result.ok( 48 | User( 49 | userId: id, 50 | username: username, 51 | ), 52 | ); 53 | } on dynamic catch (e, sc) { 54 | logError(e, sc); 55 | return Result.err(FirestoreError.error); 56 | } 57 | } 58 | 59 | @override 60 | Future> update(User user) async { 61 | try { 62 | await FirebaseFirestore.instance 63 | .collection('users') 64 | .doc(user.userId.value) 65 | .update( 66 | { 67 | _username: user.username, 68 | }, 69 | ); 70 | return Result.ok(null); 71 | } on dynamic catch (e, sc) { 72 | logError(e, sc); 73 | return Result.err(FirestoreError.error); 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_todo/run.dart'; 2 | 3 | void main() => run(); 4 | -------------------------------------------------------------------------------- /lib/model/app_error.dart: -------------------------------------------------------------------------------- 1 | // TODO(torikatsu): Errorを集約する 2 | enum AppError { 3 | unknown, 4 | unauthorized, 5 | illigalUrl; 6 | } 7 | -------------------------------------------------------------------------------- /lib/model/form_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:freezed_annotation/freezed_annotation.dart'; 3 | 4 | part '../generated/model/form_model.freezed.dart'; 5 | 6 | typedef Validator = String? Function(String value); 7 | 8 | @freezed 9 | class FormModel with _$FormModel { 10 | factory FormModel({ 11 | required Validator validator, 12 | String? additionalValidationError, 13 | required TextEditingController controller, 14 | required FocusNode focusNode, 15 | @Default(false) bool hasEdit, 16 | @Default([]) List serverErrors, 17 | }) = _FormModel; 18 | 19 | FormModel._(); 20 | 21 | void initialize(void Function(FormModel model) onChange) { 22 | focusNode 23 | .addListener(() => onChange(copyWith(hasEdit: true, serverErrors: []))); 24 | } 25 | 26 | String? get errors { 27 | if (!canDisplayError) { 28 | return null; 29 | } else if (_errors.isEmpty) { 30 | return null; 31 | } else { 32 | return _errors.join('\n'); 33 | } 34 | } 35 | 36 | late final isValid = _errors.isEmpty; 37 | late final text = controller.text; 38 | 39 | late final canDisplayError = !focusNode.hasFocus && hasEdit; 40 | 41 | List get _errors { 42 | final validationError = validator(controller.text); 43 | final addionalError = additionalValidationError; 44 | return [ 45 | if (validationError != null) 46 | validationError 47 | else if (addionalError != null) 48 | addionalError, 49 | ...serverErrors, 50 | ]; 51 | } 52 | 53 | FormModel addServerError(String e) => 54 | copyWith(serverErrors: [...serverErrors, e]); 55 | 56 | FormModel onSubmit() { 57 | focusNode.unfocus(); 58 | return copyWith(hasEdit: true); 59 | } 60 | 61 | void dispose() { 62 | controller.dispose(); 63 | focusNode.dispose(); 64 | } 65 | } 66 | 67 | FormModel createFormModel([Validator? validator, String initText = '']) { 68 | return FormModel( 69 | validator: validator ?? (_) => null, 70 | controller: TextEditingController(text: initText), 71 | focusNode: FocusNode(), 72 | ); 73 | } 74 | -------------------------------------------------------------------------------- /lib/model/query_list.dart: -------------------------------------------------------------------------------- 1 | abstract class Cursor {} 2 | 3 | class QueryList { 4 | QueryList(this.list, this.cursor, this.hasMoreData); 5 | 6 | List list; 7 | C cursor; 8 | bool hasMoreData; 9 | } 10 | -------------------------------------------------------------------------------- /lib/model/result.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | 3 | part '../generated/model/result.freezed.dart'; 4 | 5 | @freezed 6 | class Result with _$Result { 7 | factory Result.ok(Data value) = Ok; 8 | factory Result.err(Err value) = Error; 9 | 10 | T flatMap({ 11 | required T Function(Data) ok, 12 | required T Function(Err) err, 13 | }) => 14 | map( 15 | ok: (data) => ok(data.value), 16 | err: (e) => err(e.value), 17 | ); 18 | 19 | Result map_({ 20 | required T Function(Data) ok, 21 | required E Function(Err) err, 22 | }) => 23 | map( 24 | ok: (v) => Result.ok(ok(v.value)), 25 | err: (e) => Result.err(err(e.value)), 26 | ); 27 | 28 | Result._(); 29 | } 30 | -------------------------------------------------------------------------------- /lib/model/task.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_todo/model/query_list.dart'; 2 | import 'package:flutter_todo/model/result.dart'; 3 | import 'package:freezed_annotation/freezed_annotation.dart'; 4 | 5 | part '../generated/model/task.freezed.dart'; 6 | 7 | @freezed 8 | class Task with _$Task { 9 | factory Task({ 10 | required TaskId id, 11 | required String name, 12 | required DateTime createdAt, 13 | required bool isDone, 14 | }) = _Task; 15 | 16 | Task._(); 17 | 18 | factory Task.restore({ 19 | required String id, 20 | required String name, 21 | required DateTime createdAt, 22 | required bool isDone, 23 | }) => 24 | Task( 25 | id: TaskId(id), 26 | name: name, 27 | createdAt: createdAt, 28 | isDone: isDone, 29 | ); 30 | 31 | Task done() => copyWith(isDone: true); 32 | 33 | Task updateName(String name) => copyWith(name: name); 34 | } 35 | 36 | @freezed 37 | class TaskId with _$TaskId { 38 | factory TaskId(String value) = _TaskId; 39 | 40 | TaskId._(); 41 | } 42 | 43 | abstract class TaskRepository { 44 | Future, Err>> findAllTodo([C? cursor]); 45 | Future, Err>> findAllDone([C? cursor]); 46 | Future> findById(TaskId id); 47 | Future> insert({required String name}); 48 | Future> update(Task task); 49 | } 50 | -------------------------------------------------------------------------------- /lib/model/user.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_todo/model/result.dart'; 2 | import 'package:flutter_todo/model/user_auth.dart'; 3 | import 'package:freezed_annotation/freezed_annotation.dart'; 4 | 5 | part '../generated/model/user.freezed.dart'; 6 | 7 | @freezed 8 | class User with _$User { 9 | factory User({ 10 | required UserId userId, 11 | required String username, 12 | }) = _User; 13 | 14 | User._(); 15 | } 16 | 17 | abstract class UserRepository { 18 | Future> findById(UserId id); 19 | 20 | Future> register({ 21 | required UserId id, 22 | required String username, 23 | }); 24 | 25 | Future> update(User user); 26 | } 27 | 28 | @freezed 29 | class UserId with _$UserId { 30 | factory UserId(String value) = _UserId; 31 | 32 | factory UserId.fromAuthId(AuthId id) => UserId(id.value); 33 | 34 | UserId._(); 35 | } 36 | -------------------------------------------------------------------------------- /lib/model/user_auth.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | 3 | part '../generated/model/user_auth.freezed.dart'; 4 | 5 | @freezed 6 | class UserAuth with _$UserAuth { 7 | factory UserAuth({required AuthId userId}) = _UserAuth; 8 | 9 | UserAuth._(); 10 | } 11 | 12 | @freezed 13 | class AuthId with _$AuthId { 14 | factory AuthId(String value) = _AuthId; 15 | 16 | AuthId._(); 17 | } 18 | -------------------------------------------------------------------------------- /lib/model/validator.dart: -------------------------------------------------------------------------------- 1 | final emailExp = RegExp( 2 | r"^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$"); 3 | 4 | final passwordExp = RegExp(r"^(?=.*\d)(?=.*[A-Z])(?=.*[a-z])[a-zA-Z\d]{8,}$"); 5 | 6 | String? emailValidator(String? value) { 7 | if (value == null || value.isEmpty) { 8 | return '必須項目です'; 9 | } else if (!emailExp.hasMatch(value)) { 10 | return '不正な形式です'; 11 | } else { 12 | return null; 13 | } 14 | } 15 | 16 | String? passwordValidator(String? value) { 17 | if (value == null || value.isEmpty) { 18 | return '必須項目です'; 19 | } else if (!passwordExp.hasMatch(value)) { 20 | return '半角英数字(大文字小文字)8文字以上で入力してください'; 21 | } else { 22 | return null; 23 | } 24 | } 25 | 26 | String? confirmPasswordValidator(String? password, String? confirmPassword) { 27 | if (confirmPassword == null || confirmPassword.isEmpty) { 28 | return '必須項目です'; 29 | } else if (password != confirmPassword) { 30 | return 'パスワードが一致しません'; 31 | } else { 32 | return null; 33 | } 34 | } 35 | 36 | String? mandatoryValidator(String? value) { 37 | if (value == null || value.isEmpty) { 38 | return '必須項目です'; 39 | } else { 40 | return null; 41 | } 42 | } -------------------------------------------------------------------------------- /lib/provider/global_controller/date_format_provider.dart: -------------------------------------------------------------------------------- 1 | import 'package:intl/intl.dart'; 2 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 3 | 4 | final dateFormatProvider = 5 | Provider((_) => DateFormat('yyyy-MM-dd HH:mm')); 6 | -------------------------------------------------------------------------------- /lib/provider/global_controller/loading_provider.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/foundation.dart'; 2 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 3 | 4 | final loadingProvider = 5 | StateNotifierProvider((_) => LoadingNotifier()); 6 | 7 | class LoadingNotifier extends StateNotifier { 8 | LoadingNotifier() : super(false); 9 | 10 | Future run(AsyncCallback asyncCallback) async { 11 | state = true; 12 | await asyncCallback(); 13 | state = false; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /lib/provider/global_controller/network_dialog_provider.dart: -------------------------------------------------------------------------------- 1 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 2 | 3 | final networkDialogProvider = 4 | StateNotifierProvider( 5 | (_) => NetworkDialogNotifier()); 6 | 7 | class NetworkDialogNotifier extends StateNotifier { 8 | NetworkDialogNotifier() : super(false); 9 | 10 | void show() { 11 | state = true; 12 | } 13 | 14 | void hide() { 15 | state = false; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /lib/provider/infrastructure/user_provider.dart: -------------------------------------------------------------------------------- 1 | import 'package:firebase_auth/firebase_auth.dart' as auth; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_todo/model/user_auth.dart'; 4 | import 'package:flutter_todo/provider/model/user_repository_provider.dart'; 5 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 6 | import 'package:flutter_todo/model/user.dart'; 7 | 8 | class _UserStateNotifier extends ValueNotifier { 9 | _UserStateNotifier() : super(null); 10 | void notify() => notifyListeners(); 11 | } 12 | 13 | final userStateNotifier = _UserStateNotifier(); 14 | 15 | final authProvider = StreamProvider( 16 | (ref) { 17 | return auth.FirebaseAuth.instance.authStateChanges().map( 18 | (v) { 19 | final maybeAuth = v; 20 | if (maybeAuth == null) { 21 | return null; 22 | } else { 23 | return UserAuth(userId: AuthId(maybeAuth.uid)); 24 | } 25 | }, 26 | ); 27 | }, 28 | ); 29 | 30 | final userProvider = StreamProvider( 31 | (ref) { 32 | ref.listenSelf((_, v) => userStateNotifier.notify()); 33 | return ref.watch(authProvider.stream).asyncMap( 34 | (auth) async { 35 | if (auth == null) return null; 36 | final userId = UserId.fromAuthId(auth.userId); 37 | final result = await ref.read(userRepositoryProvider).findById(userId); 38 | return result.map( 39 | ok: (user) => user.value, 40 | // TODO(torikatsu): handle error correctory 41 | err: (e) => null, 42 | ); 43 | }, 44 | ); 45 | }, 46 | ); 47 | -------------------------------------------------------------------------------- /lib/provider/local/local_provider.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_todo/model/task.dart'; 2 | import 'package:flutter_todo/model/user.dart'; 3 | import 'package:flutter_todo/model/user_auth.dart'; 4 | import 'package:flutter_todo/provider/route/pram.dart'; 5 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 6 | 7 | final localAuthProvider = Provider( 8 | (ref) => throw UnimplementedError(), 9 | ); 10 | 11 | final localUserProvider = Provider( 12 | (ref) => throw UnimplementedError(), 13 | ); 14 | 15 | final localTaskIdParamProvier = Provider( 16 | (ref) => throw UnimplementedError(), 17 | ); 18 | 19 | final localTabParamProvier = Provider( 20 | (ref) => throw UnimplementedError(), 21 | ); 22 | final localInnerTabProvier = Provider( 23 | (ref) => throw UnimplementedError(), 24 | ); 25 | -------------------------------------------------------------------------------- /lib/provider/model/task_provider.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_todo/infrastructure/firestore_error.dart'; 2 | import 'package:flutter_todo/model/result.dart'; 3 | import 'package:flutter_todo/model/user.dart'; 4 | import 'package:flutter_todo/util/pagenated_list_controller.dart'; 5 | import 'package:flutter_todo/util/tupple.dart'; 6 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 7 | 8 | import 'package:flutter_todo/provider/model/task_repository_provider.dart'; 9 | import 'package:flutter_todo/model/task.dart'; 10 | 11 | final todoTasksFamily = StateNotifierProvider.family< 12 | PagenatedListController, 13 | AsyncPagenatedList, 14 | UserId>( 15 | (ref, userId) => PagenatedListController( 16 | ref.watch(taskRepositoryFamily(userId)).findAllTodo, 17 | ), 18 | ); 19 | 20 | final doneTasksFamily = StateNotifierProvider.family< 21 | PagenatedListController, 22 | AsyncPagenatedList, 23 | UserId>( 24 | (ref, userId) => PagenatedListController( 25 | ref.watch(taskRepositoryFamily(userId)).findAllDone, 26 | ), 27 | ); 28 | 29 | final taskFamily = 30 | FutureProvider.family, T2>( 31 | (ref, arg) => ref.watch(taskRepositoryFamily(arg.v1)).findById(arg.v2), 32 | ); 33 | -------------------------------------------------------------------------------- /lib/provider/model/task_repository_provider.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_todo/infrastructure/cursor_impl.dart'; 2 | import 'package:flutter_todo/infrastructure/firestore_error.dart'; 3 | import 'package:flutter_todo/infrastructure/task_repository_impl.dart'; 4 | import 'package:flutter_todo/model/task.dart'; 5 | import 'package:flutter_todo/model/user.dart'; 6 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 7 | 8 | final taskRepositoryFamily = 9 | Provider.family, UserId>( 10 | (_, userId) => TaskRepositoryImpl(userId), 11 | ); 12 | -------------------------------------------------------------------------------- /lib/provider/model/user_repository_provider.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_todo/infrastructure/user_repository_impl.dart'; 2 | import 'package:flutter_todo/model/user.dart'; 3 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 4 | 5 | final userRepositoryProvider = Provider( 6 | (ref) => UserRepositoryImpl(), 7 | ); 8 | -------------------------------------------------------------------------------- /lib/provider/route/guard.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_todo/provider/infrastructure/user_provider.dart'; 2 | import 'package:flutter_todo/provider/route/pram.dart'; 3 | import 'package:flutter_todo/provider/route/routes.dart'; 4 | import 'package:go_router/go_router.dart'; 5 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 6 | 7 | typedef RouteGuard = String? Function(Ref ref, GoRouterState state); 8 | 9 | String _deafultHomePath(GoRouterState state) { 10 | return state.namedLocation( 11 | Routes.home.value, 12 | params: { 13 | ParamKeys.tab.value: HomeTab.task.value, 14 | }, 15 | ); 16 | } 17 | 18 | String? combineGuard(GoRouterState state, Ref ref, List guards) { 19 | for (final guard in guards) { 20 | final redirectTo = guard(ref, state); 21 | if (redirectTo != null) { 22 | return redirectTo; 23 | } 24 | } 25 | return null; 26 | } 27 | 28 | String? authGuard(Ref ref, GoRouterState state) { 29 | final isNotAuthenticated = ref.read(authProvider).value == null; 30 | if (isNotAuthenticated) { 31 | return state.namedLocation(Routes.signIn.value); 32 | } else { 33 | return null; 34 | } 35 | } 36 | 37 | String? noAuthGuard(Ref ref, GoRouterState state) { 38 | final isAuthenticated = ref.read(authProvider).value != null; 39 | if (isAuthenticated) { 40 | return _deafultHomePath(state); 41 | } else { 42 | return null; 43 | } 44 | } 45 | 46 | String? userGuard(Ref ref, GoRouterState state) { 47 | final isNotRegistered = ref.read(userProvider).value == null; 48 | if (isNotRegistered) { 49 | return state.namedLocation(Routes.register.value); 50 | } else { 51 | return null; 52 | } 53 | } 54 | 55 | String? noUserGuard(Ref ref, GoRouterState state) { 56 | final isRegistered = ref.read(userProvider).value != null; 57 | if (isRegistered) { 58 | return _deafultHomePath(state); 59 | } else { 60 | return null; 61 | } 62 | } 63 | 64 | String? todoTabGuard( 65 | Ref ref, 66 | GoRouterState state, 67 | ) { 68 | final isNotTaskTab = 69 | HomeTab.parse(state.params[ParamKeys.tab.value]) != HomeTab.task; 70 | if (isNotTaskTab) { 71 | return state.namedLocation(Routes.notFound.value); 72 | } else { 73 | return null; 74 | } 75 | } 76 | 77 | String? myPageTabGuard(Ref ref, GoRouterState state) { 78 | final isNotMyPageTab = 79 | HomeTab.parse(state.params[ParamKeys.tab.value]) != HomeTab.mypage; 80 | if (isNotMyPageTab) { 81 | return state.namedLocation(Routes.notFound.value); 82 | } else { 83 | return null; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /lib/provider/route/local_provider_scope.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_todo/model/task.dart'; 3 | import 'package:flutter_todo/provider/infrastructure/user_provider.dart'; 4 | import 'package:flutter_todo/provider/local/local_provider.dart'; 5 | import 'package:flutter_todo/provider/route/pram.dart'; 6 | import 'package:flutter_todo/provider/route/routes.dart'; 7 | import 'package:go_router/go_router.dart'; 8 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 9 | 10 | class LocalProviderScope extends ConsumerWidget { 11 | const LocalProviderScope({ 12 | required this.state, 13 | required this.child, 14 | super.key, 15 | }); 16 | 17 | final GoRouterState state; 18 | final Widget child; 19 | 20 | @override 21 | Widget build(BuildContext context, WidgetRef ref) { 22 | final maybeAuth = ref.read(authProvider).value; 23 | final maybeUser = ref.read(userProvider).value; 24 | final maybeTab = HomeTab.parse(state.params[ParamKeys.tab.value]); 25 | final maybeTaskId = state.params[ParamKeys.taskId.value]; 26 | final innerTab = InnerTab.parse(state.queryParams[QueryParamKeys.innerTab]); 27 | return ProviderScope( 28 | overrides: [ 29 | if (maybeAuth != null) localAuthProvider.overrideWithValue(maybeAuth), 30 | if (maybeUser != null) localUserProvider.overrideWithValue(maybeUser), 31 | if (maybeTab != null) localTabParamProvier.overrideWithValue(maybeTab), 32 | if (maybeTaskId != null) 33 | localTaskIdParamProvier.overrideWithValue(TaskId(maybeTaskId)), 34 | localInnerTabProvier.overrideWithValue(innerTab), 35 | ], 36 | child: child, 37 | ); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /lib/provider/route/pram.dart: -------------------------------------------------------------------------------- 1 | enum HomeTab { 2 | task(0, 'task'), 3 | mypage(1, 'mypage'); 4 | 5 | const HomeTab(this.i, this.value); 6 | 7 | final int i; 8 | final String value; 9 | 10 | factory HomeTab.fromIndex(int index) { 11 | return HomeTab.values[index]; 12 | } 13 | 14 | static HomeTab? parse(String? param) { 15 | try { 16 | return HomeTab.values.firstWhere( 17 | (e) => e.value == param, 18 | ); 19 | } catch (e) { 20 | return null; 21 | } 22 | } 23 | } 24 | 25 | enum InnerTab { 26 | todo(0, 'todo'), 27 | done(1, 'done'); 28 | 29 | const InnerTab(this.i, this.value); 30 | 31 | final int i; 32 | final String value; 33 | 34 | static const _defaultInnerTab = todo; 35 | 36 | factory InnerTab.fromIndex(int index) { 37 | return InnerTab.values[index]; 38 | } 39 | 40 | static InnerTab parse(String? param) { 41 | return InnerTab.values.firstWhere( 42 | (e) => e.value == param, 43 | orElse: () => InnerTab._defaultInnerTab, 44 | ); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /lib/provider/route/router_provider.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/foundation.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_todo/provider/infrastructure/user_provider.dart'; 4 | import 'package:flutter_todo/provider/route/local_provider_scope.dart'; 5 | import 'package:flutter_todo/provider/route/pram.dart'; 6 | import 'package:flutter_todo/provider/route/routes.dart'; 7 | import 'package:flutter_todo/view/component/loading_overlay.dart'; 8 | import 'package:flutter_todo/view/dialog/network_alert_dialog.dart'; 9 | import 'package:flutter_todo/view/page/debug_page/debug_page.dart'; 10 | import 'package:flutter_todo/view/page/edit_task_page/edit_task_page.dart'; 11 | import 'package:flutter_todo/view/page/register_page/register_page.dart'; 12 | import 'package:flutter_todo/view/page/task_detail_page/task_detail_page.dart'; 13 | import 'package:flutter_todo/provider/route/guard.dart'; 14 | import 'package:go_router/go_router.dart'; 15 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 16 | import 'package:flutter_todo/view/page/error_page/error_page.dart'; 17 | import 'package:flutter_todo/view/page/home_page/home_page.dart'; 18 | import 'package:flutter_todo/view/page/create_task_page/create_task_page.dart'; 19 | import 'package:flutter_todo/view/page/notfound_page/notfound_page.dart'; 20 | import 'package:flutter_todo/view/page/setting_page/setting_page.dart'; 21 | import 'package:flutter_todo/view/page/signin_page/signin_page.dart'; 22 | import 'package:flutter_todo/view/page/signup_page/signup_page.dart'; 23 | 24 | final routerProvider = Provider( 25 | (ref) { 26 | return GoRouter( 27 | routes: [ 28 | GoRoute( 29 | path: '/', 30 | redirect: (state) => state.namedLocation( 31 | Routes.home.value, 32 | params: { 33 | ParamKeys.tab.value: HomeTab.task.value, 34 | }, 35 | ), 36 | ), 37 | GoRoute( 38 | path: '/notfound', 39 | name: Routes.notFound.value, 40 | builder: _builder(const NotFoundPage()), 41 | ), 42 | GoRoute( 43 | path: '/signin', 44 | name: Routes.signIn.value, 45 | redirect: (state) => combineGuard( 46 | state, 47 | ref, 48 | [ 49 | noAuthGuard, 50 | ], 51 | ), 52 | builder: _builder(const SigninPage()), 53 | ), 54 | GoRoute( 55 | path: '/signup', 56 | name: Routes.signUp.value, 57 | redirect: (state) => combineGuard( 58 | state, 59 | ref, 60 | [ 61 | noAuthGuard, 62 | ], 63 | ), 64 | builder: _builder(const SignupPage()), 65 | ), 66 | GoRoute( 67 | path: '/register', 68 | name: Routes.register.value, 69 | redirect: (state) => combineGuard( 70 | state, 71 | ref, 72 | [ 73 | authGuard, 74 | noUserGuard, 75 | ], 76 | ), 77 | builder: _builder(const RegisterPage()), 78 | ), 79 | GoRoute( 80 | path: '/home', 81 | redirect: (state) => state.namedLocation( 82 | Routes.home.value, 83 | params: { 84 | ParamKeys.tab.value: HomeTab.task.value, 85 | }, 86 | ), 87 | ), 88 | GoRoute( 89 | path: '/home/:tab', 90 | name: Routes.home.value, 91 | redirect: (state) => combineGuard( 92 | state, 93 | ref, 94 | [ 95 | authGuard, 96 | userGuard, 97 | ], 98 | ), 99 | builder: _builder(const HomePage()), 100 | routes: [ 101 | GoRoute( 102 | path: 'create', 103 | name: Routes.taskCreate.value, 104 | redirect: (state) => combineGuard( 105 | state, 106 | ref, 107 | [ 108 | authGuard, 109 | userGuard, 110 | todoTabGuard, 111 | ], 112 | ), 113 | builder: _builder(const CreatePage()), 114 | ), 115 | GoRoute( 116 | path: 'setting', 117 | name: Routes.setting.value, 118 | redirect: (state) => combineGuard( 119 | state, 120 | ref, 121 | [ 122 | authGuard, 123 | userGuard, 124 | myPageTabGuard, 125 | ], 126 | ), 127 | builder: _builder(const SettingPage()), 128 | ), 129 | GoRoute( 130 | path: ':taskId', 131 | name: Routes.taskDetail.value, 132 | redirect: (state) => combineGuard( 133 | state, 134 | ref, 135 | [ 136 | authGuard, 137 | userGuard, 138 | todoTabGuard, 139 | ], 140 | ), 141 | builder: _builder(const TaskDetailPage()), 142 | routes: [ 143 | GoRoute( 144 | path: 'edit', 145 | name: Routes.taskEdit.value, 146 | redirect: (state) => combineGuard( 147 | state, 148 | ref, 149 | [ 150 | authGuard, 151 | userGuard, 152 | todoTabGuard, 153 | ], 154 | ), 155 | builder: _builder(const EditTaskPage()), 156 | ), 157 | ], 158 | ), 159 | ], 160 | ), 161 | if (kDebugMode) 162 | GoRoute( 163 | path: '/debug', 164 | name: Routes.debug.value, 165 | builder: _builder(const DebugPage()), 166 | ), 167 | ], 168 | errorBuilder: _builder(const ErrorPage()), 169 | urlPathStrategy: UrlPathStrategy.path, 170 | debugLogDiagnostics: true, 171 | initialLocation: '/signin', 172 | refreshListenable: userStateNotifier, 173 | navigatorBuilder: navigatorBuilder, 174 | ); 175 | }, 176 | ); 177 | 178 | extension GoRouterExt on GoRouter { 179 | void goNamed_( 180 | Routes name, { 181 | Map params = const {}, 182 | Map queryParams = const {}, 183 | bool isMaintainQuery = true, 184 | }) { 185 | final paramsMap = params.map((key, value) => MapEntry(key.value, value)); 186 | final queryParamsMap = 187 | queryParams.map((key, value) => MapEntry(key.value, value)); 188 | final currentQuery = 189 | routerDelegate.currentConfiguration.location.queryParameters; 190 | 191 | final nextQuery = { 192 | ...(isMaintainQuery ? currentQuery : {}), 193 | ...queryParamsMap 194 | }; 195 | 196 | goNamed(name.value, params: paramsMap, queryParams: nextQuery); 197 | } 198 | 199 | // TODO(torikatsu): maintain query 200 | void pop_() => routerDelegate.pop(); 201 | } 202 | 203 | Widget Function(BuildContext, GoRouterState) _builder(Widget child) { 204 | return (BuildContext context, GoRouterState state) { 205 | return LocalProviderScope( 206 | state: state, 207 | child: child, 208 | ); 209 | }; 210 | } 211 | 212 | Widget navigatorBuilder(_, __, Widget child) { 213 | return Stack( 214 | children: [ 215 | child, 216 | const LoadingOverlay(), 217 | const NetworkAlertDialog(), 218 | ], 219 | ); 220 | } 221 | -------------------------------------------------------------------------------- /lib/provider/route/routes.dart: -------------------------------------------------------------------------------- 1 | enum Routes { 2 | notFound('notFound'), 3 | signIn('signIn'), 4 | signUp('signUp'), 5 | register('register'), 6 | home('home'), 7 | taskCreate('taskCreate'), 8 | taskDetail('taskDetail'), 9 | taskEdit('taskEdit'), 10 | setting('setting'), 11 | debug('debug'); 12 | 13 | const Routes(this.value); 14 | final String value; 15 | } 16 | 17 | enum ParamKeys { 18 | tab('tab'), 19 | taskId('taskId'); 20 | 21 | const ParamKeys(this.value); 22 | final String value; 23 | } 24 | 25 | enum QueryParamKeys { 26 | innerTab('innerTab'); 27 | 28 | const QueryParamKeys(this.value); 29 | final String value; 30 | } -------------------------------------------------------------------------------- /lib/run.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | import 'package:flutter_todo/app.dart'; 3 | import 'package:flutter_todo/infrastructure/firebase.dart'; 4 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 5 | 6 | void run() async { 7 | WidgetsFlutterBinding.ensureInitialized(); 8 | await initializeFirebase(); 9 | 10 | runApp( 11 | const ProviderScope( 12 | child: App(), 13 | ), 14 | ); 15 | } 16 | -------------------------------------------------------------------------------- /lib/util/async_value.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_todo/infrastructure/firestore_error.dart'; 2 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 3 | import 'package:flutter_todo/model/result.dart'; 4 | 5 | extension AsyncValueExt on AsyncValue> { 6 | T maybeFlatMap({ 7 | required T Function(Data data) data, 8 | T Function()? loading, 9 | required T orElse, 10 | }) => 11 | maybeMap( 12 | data: (v) => v.value.map( 13 | ok: (ok) => data(ok.value), 14 | err: (e) => orElse, 15 | ), 16 | loading: (_) { 17 | if (loading != null) { 18 | return loading(); 19 | } else { 20 | return orElse; 21 | } 22 | }, 23 | orElse: () => orElse, 24 | ); 25 | 26 | AsyncValue> flatMapData( 27 | T Function(Data) functor, 28 | ) => 29 | map( 30 | data: (v) => v.whenData( 31 | (v) => v.map_(ok: functor, err: (e) => e), 32 | ), 33 | loading: (_) => AsyncValue>.loading(), 34 | error: (e) => 35 | AsyncValue>.error(e, StackTrace.empty), 36 | ); 37 | 38 | void flatWhenData( 39 | void Function(Data) functor, 40 | ) => 41 | map( 42 | data: (v) => v.value.maybeWhen( 43 | ok: functor, 44 | orElse: () => null, 45 | ), 46 | loading: (_) {}, 47 | error: (_) {}, 48 | ); 49 | } 50 | 51 | extension AsyncValueWithFirestoreErrorExt 52 | on AsyncValue> { 53 | T flatMap({ 54 | required T Function(Data data) data, 55 | required T Function() loading, 56 | T Function()? notFound, 57 | T Function()? error, 58 | T Function()? orElse, 59 | }) => 60 | maybeMap( 61 | data: (v) => v.value.map( 62 | ok: (ok) => data(ok.value), 63 | err: (e) => e.value.maybeMap( 64 | notFound: error, 65 | error: notFound, 66 | orElse: orElse, 67 | ), 68 | ), 69 | orElse: loading, 70 | ); 71 | } 72 | -------------------------------------------------------------------------------- /lib/util/env.dart: -------------------------------------------------------------------------------- 1 | class Env { 2 | static const isEmulator = 3 | bool.fromEnvironment('USE_FIREBASE_EMULATOR', defaultValue: false); 4 | } 5 | -------------------------------------------------------------------------------- /lib/util/logger.dart: -------------------------------------------------------------------------------- 1 | import 'dart:developer'; 2 | 3 | void logError(e, sc) { 4 | log('unhandled error has occured', 5 | error: e, stackTrace: sc, name: 'flutter_todo', level: 2000); 6 | } 7 | -------------------------------------------------------------------------------- /lib/util/pagenated_list_controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_todo/infrastructure/cursor_impl.dart'; 2 | import 'package:flutter_todo/util/async_value.dart'; 3 | import 'package:flutter_todo/model/query_list.dart'; 4 | import 'package:flutter_todo/model/result.dart'; 5 | import 'package:flutter_todo/util/util.dart'; 6 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 7 | import 'package:freezed_annotation/freezed_annotation.dart'; 8 | 9 | part '../generated/util/pagenated_list_controller.freezed.dart'; 10 | 11 | class PagenatedListController 12 | extends StateNotifier> { 13 | PagenatedListController(this._fetch) : super(const AsyncValue.loading()) { 14 | _initialize(); 15 | } 16 | 17 | final Fetch _fetch; 18 | 19 | Future _initialize() async { 20 | state = (await _fetch()).flatMap( 21 | ok: (data) => AsyncValue.data( 22 | Result.ok( 23 | PagenatedList(list: data.list, cursor: data.cursor), 24 | ), 25 | ), 26 | err: (e) => AsyncValue.data(Result.err(e)), 27 | ); 28 | } 29 | 30 | Future refresh() async { 31 | if (!state.maybeFlatMap(data: (v) => v.canRefresh, orElse: false)) return; 32 | state = state.flatMapData((data) => data.copyWith(isRefreshing: true)); 33 | final result = await _fetch(); 34 | state = state.flatMapData( 35 | (state) => result.flatMap( 36 | ok: (ok) => state.copyWith( 37 | list: ok.list, 38 | cursor: ok.cursor, 39 | refreshError: null, 40 | isRefreshing: false, 41 | ), 42 | err: (e) => state.copyWith( 43 | refreshError: e, 44 | isRefreshing: false, 45 | ), 46 | ), 47 | ); 48 | } 49 | 50 | void resolveAndLoadMore() async { 51 | state = state.flatMapData((data) => data.copyWith(loadMoreError: null)); 52 | await loadMore(); 53 | } 54 | 55 | Future loadMore() async { 56 | if (!state.maybeFlatMap(data: (data) => data.canLoadMore, orElse: false)) { 57 | return; 58 | } 59 | state = state.flatMapData((data) => data.copyWith(isMoreLoading: true)); 60 | final cursor = 61 | state.maybeFlatMap(data: (data) => data.cursor, orElse: null); 62 | final result = await _fetch(cursor); 63 | state = state.flatMapData( 64 | (data) => result.map( 65 | ok: (result) => data.copyWith( 66 | list: [...data.list, ...result.value.list], 67 | cursor: result.value.cursor, 68 | hasMoreData: result.value.hasMoreData, 69 | isMoreLoading: false, 70 | ), 71 | err: (e) => data.copyWith( 72 | loadMoreError: e.value, 73 | isMoreLoading: false, 74 | ), 75 | ), 76 | ); 77 | } 78 | 79 | void insert(Item item) => state = state.flatMapData( 80 | (state) => state.copyWith( 81 | list: [item, ...state.list], 82 | ), 83 | ); 84 | 85 | void update(bool Function(Item) isTarget, Item next) => 86 | state = state.flatMapData( 87 | (data) => data.copyWith( 88 | list: data.list.map((item) => isTarget(item) ? next : item).toList(), 89 | ), 90 | ); 91 | 92 | void delete(Item item) => state = state.flatMapData( 93 | (data) => data.copyWith( 94 | list: data.list.where((e) => e != item).toList(), 95 | ), 96 | ); 97 | } 98 | 99 | @freezed 100 | class PagenatedList with _$PagenatedList { 101 | factory PagenatedList({ 102 | @Default([]) List list, 103 | CursorImpl? cursor, 104 | @Default(false) isRefreshing, 105 | @Default(false) isMoreLoading, 106 | @Default(true) hasMoreData, 107 | @Default(null) Err? refreshError, 108 | @Default(null) Err? loadMoreError, 109 | }) = _PagenatedList; 110 | 111 | late final bool hasLoadMoreError = isNotNull(loadMoreError); 112 | late final bool hasRefreshError = isNotNull(refreshError); 113 | late final bool canLoadMore = 114 | !isMoreLoading && !hasLoadMoreError && hasMoreData; 115 | late final bool canRefresh = !isRefreshing; 116 | 117 | PagenatedList._(); 118 | } 119 | 120 | typedef AsyncPagenatedList 121 | = AsyncValue, Err>>; 122 | 123 | typedef Fetch = Future, Err>> 124 | Function([CursorImpl? impl]); 125 | -------------------------------------------------------------------------------- /lib/util/tupple.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | 3 | part '../generated/util/tupple.freezed.dart'; 4 | 5 | @freezed 6 | class T2 with _$T2 { 7 | factory T2( 8 | V1 v1, 9 | V2 v2, 10 | ) = _T2; 11 | 12 | T2._(); 13 | } 14 | -------------------------------------------------------------------------------- /lib/util/util.dart: -------------------------------------------------------------------------------- 1 | bool isNull(T v) => v == null; 2 | 3 | bool isNotNull(T v) => !isNull(v); 4 | -------------------------------------------------------------------------------- /lib/view/component/custom_modal_barriere.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class CustomModalBarriere extends StatefulWidget { 4 | const CustomModalBarriere( 5 | {required this.show, required this.onTap, required this.child, Key? key}) 6 | : super(key: key); 7 | 8 | final bool show; 9 | final VoidCallback onTap; 10 | final Widget child; 11 | 12 | @override 13 | State createState() => _CustomModalBarriereState(); 14 | } 15 | 16 | const duration = Duration(milliseconds: 150); 17 | 18 | class _CustomModalBarriereState extends State 19 | with TickerProviderStateMixin { 20 | late final AnimationController _controller; 21 | late final Animation animatedOpacity; 22 | 23 | late String currentColor; 24 | 25 | bool show = false; 26 | 27 | @override 28 | void initState() { 29 | super.initState(); 30 | _controller = AnimationController( 31 | vsync: this, 32 | duration: duration, 33 | ); 34 | animatedOpacity = Tween(begin: 0, end: 1).animate(_controller) 35 | ..addListener(() => setState(() {})); 36 | } 37 | 38 | @override 39 | void didUpdateWidget(covariant CustomModalBarriere oldWidget) { 40 | super.didUpdateWidget(oldWidget); 41 | if (widget.show) { 42 | _controller.forward(); 43 | show = true; 44 | } else { 45 | _controller.reverse().whenCompleteOrCancel( 46 | () => setState(() => show = false), 47 | ); 48 | } 49 | } 50 | 51 | @override 52 | Widget build(BuildContext context) { 53 | if (show) { 54 | return Opacity( 55 | opacity: animatedOpacity.value, 56 | child: Stack( 57 | fit: StackFit.expand, 58 | children: [ 59 | GestureDetector( 60 | onTap: widget.onTap, 61 | child: ColoredBox( 62 | color: Colors.black.withOpacity(0.3), 63 | ), 64 | ), 65 | Positioned( 66 | top: 0, 67 | right: 0, 68 | bottom: 0, 69 | left: 0, 70 | child: widget.child, 71 | ), 72 | ], 73 | ), 74 | ); 75 | } else { 76 | return const SizedBox.shrink(); 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /lib/view/component/error_view.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class ErrorView extends StatelessWidget { 4 | const ErrorView({super.key}); 5 | 6 | @override 7 | Widget build(BuildContext context) { 8 | // TODO(torikatsu): handle AppError 9 | return const Center( 10 | child: Text('error has occured'), 11 | ); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /lib/view/component/lazy_indexed_stack.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class LazyIndexedStack extends StatefulWidget { 4 | const LazyIndexedStack( 5 | {required this.builders, required this.index, Key? key}) 6 | : super(key: key); 7 | 8 | final List builders; 9 | final int index; 10 | 11 | @override 12 | State createState() => _LazyIndexedStackState(); 13 | } 14 | 15 | class _LazyIndexedStackState extends State { 16 | late final List _cache = List.filled(builders.length, null); 17 | final defaultWidget = const SizedBox.shrink(); 18 | 19 | get index => widget.index; 20 | get builders => widget.builders; 21 | 22 | @override 23 | Widget build(BuildContext context) { 24 | assert(0 <= index && index < builders.length); 25 | final target = _cache[index]; 26 | if (target == null) { 27 | _cache[index] = builders[index](context); 28 | } 29 | return IndexedStack( 30 | index: widget.index, 31 | children: _cache.map((e) => e ?? defaultWidget).toList(), 32 | ); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /lib/view/component/loading_overlay.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_todo/provider/global_controller/loading_provider.dart'; 3 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 4 | 5 | class LoadingOverlay extends ConsumerWidget { 6 | const LoadingOverlay({super.key}); 7 | 8 | @override 9 | Widget build(BuildContext context, WidgetRef ref) { 10 | final isShow = ref.watch(loadingProvider); 11 | if (isShow) { 12 | return const Indicator(); 13 | } else { 14 | return const SizedBox.shrink(); 15 | } 16 | } 17 | } 18 | 19 | class Indicator extends ConsumerWidget { 20 | const Indicator({super.key}); 21 | 22 | @override 23 | Widget build(BuildContext context, WidgetRef ref) { 24 | return ColoredBox( 25 | color: Colors.black.withOpacity(0.3), 26 | child: const Center( 27 | child: CircularProgressIndicator(), 28 | ), 29 | ); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /lib/view/component/loading_view.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class LoadingView extends StatelessWidget { 4 | const LoadingView({super.key}); 5 | 6 | @override 7 | Widget build(BuildContext context) { 8 | return const Center( 9 | child: CircularProgressIndicator(), 10 | ); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /lib/view/component/my_form.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_todo/model/form_model.dart'; 3 | 4 | class MyTextField extends StatelessWidget { 5 | const MyTextField({ 6 | required this.model, 7 | this.decoration = const InputDecoration(), 8 | super.key, 9 | }); 10 | 11 | final FormModel model; 12 | final InputDecoration decoration; 13 | 14 | @override 15 | Widget build(BuildContext context) { 16 | return TextField( 17 | controller: model.controller, 18 | focusNode: model.focusNode, 19 | decoration: decoration.copyWith( 20 | errorText: model.errors, 21 | ), 22 | ); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /lib/view/component/not_found_view.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class NotFoundView extends StatelessWidget { 4 | const NotFoundView({super.key}); 5 | 6 | @override 7 | Widget build(BuildContext context) { 8 | return const Center( 9 | child: Text('not found'), 10 | ); 11 | } 12 | } -------------------------------------------------------------------------------- /lib/view/component/task_list_item.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_todo/model/task.dart'; 3 | import 'package:flutter_todo/provider/global_controller/date_format_provider.dart'; 4 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 5 | 6 | class TaskListItem extends ConsumerWidget { 7 | const TaskListItem({required this.task, required this.onTap, super.key}); 8 | 9 | final Task task; 10 | final void Function(Task) onTap; 11 | 12 | @override 13 | Widget build(BuildContext context, WidgetRef ref) { 14 | return Container( 15 | decoration: const BoxDecoration( 16 | border: Border( 17 | bottom: BorderSide( 18 | width: 1, 19 | color: Color.fromRGBO(0, 0, 0, 0.1), 20 | ), 21 | ), 22 | ), 23 | child: ListTile( 24 | leading: Text(task.name), 25 | trailing: Text( 26 | ref.watch(dateFormatProvider).format(task.createdAt), 27 | ), 28 | onTap: () => onTap(task), 29 | ), 30 | ); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /lib/view/dialog/network_alert_dialog.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_todo/view/component/custom_modal_barriere.dart'; 3 | import 'package:flutter_todo/provider/global_controller/network_dialog_provider.dart'; 4 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 5 | 6 | class NetworkAlertDialog extends ConsumerWidget { 7 | const NetworkAlertDialog({super.key}); 8 | 9 | @override 10 | Widget build(context, ref) { 11 | final state = ref.watch(networkDialogProvider); 12 | final controller = ref.read(networkDialogProvider.notifier); 13 | return CustomModalBarriere( 14 | show: state, 15 | onTap: controller.hide, 16 | child: MaterialApp( 17 | home: AlertDialog( 18 | title: const Text('ネットワークエラー'), 19 | content: const Text('通信に失敗しました。時間をあけてお試しください'), 20 | actions: [ 21 | TextButton( 22 | onPressed: controller.hide, 23 | child: const Text('ok'), 24 | ), 25 | ], 26 | ), 27 | ), 28 | ); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /lib/view/page/create_task_page/create_task_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_todo/view/component/my_form.dart'; 3 | import 'package:flutter_todo/view/page/create_task_page/create_task_page_controller.dart'; 4 | import 'package:flutter_todo/provider/local/local_provider.dart'; 5 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 6 | 7 | class CreatePage extends ConsumerWidget { 8 | const CreatePage({super.key}); 9 | 10 | @override 11 | Widget build(context, ref) { 12 | final userId = ref.watch(localUserProvider).userId; 13 | final state = ref.watch(createTaskControllerFamily(userId)); 14 | final controller = ref.read(createTaskControllerFamily(userId).notifier); 15 | return Scaffold( 16 | appBar: AppBar( 17 | title: const Text('create'), 18 | ), 19 | body: Column( 20 | children: [ 21 | MyTextField( 22 | model: state.name, 23 | decoration: const InputDecoration( 24 | hintText: 'task name', 25 | helperText: 'task name', 26 | label: Text('task name'), 27 | ), 28 | ), 29 | ElevatedButton( 30 | onPressed: controller.onSubmit, 31 | child: const Text('submit'), 32 | ), 33 | ], 34 | ), 35 | ); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /lib/view/page/create_task_page/create_task_page_controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_todo/model/user.dart'; 2 | import 'package:flutter_todo/provider/model/task_provider.dart'; 3 | import 'package:flutter_todo/provider/model/task_repository_provider.dart'; 4 | import 'package:flutter_todo/model/form_model.dart'; 5 | import 'package:flutter_todo/model/validator.dart'; 6 | import 'package:flutter_todo/provider/global_controller/loading_provider.dart'; 7 | import 'package:flutter_todo/provider/route/pram.dart'; 8 | import 'package:flutter_todo/provider/route/router_provider.dart'; 9 | import 'package:flutter_todo/provider/route/routes.dart'; 10 | import 'package:freezed_annotation/freezed_annotation.dart'; 11 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 12 | 13 | part '../../../generated/view/page/create_task_page/create_task_page_controller.freezed.dart'; 14 | 15 | final createTaskControllerFamily = StateNotifierProvider.autoDispose 16 | .family( 17 | CreateTaskController.new, 18 | ); 19 | 20 | class CreateTaskController extends StateNotifier<_CreateTaskState> { 21 | CreateTaskController(this._ref, this.userId) 22 | : super(_CreateTaskState(name: createFormModel(mandatoryValidator))) { 23 | state.name.initialize(_onChangeName); 24 | } 25 | 26 | final Ref _ref; 27 | final UserId userId; 28 | 29 | _onChangeName(FormModel name) => state = state.copyWith(name: name); 30 | 31 | Future onSubmit() async { 32 | state = state.onSubmit(); 33 | if (!state.isValidAll) { 34 | return; 35 | } 36 | await _ref.read(loadingProvider.notifier).run( 37 | () async { 38 | final result = await _ref 39 | .read(taskRepositoryFamily(userId)) 40 | .insert(name: state.name.text); 41 | result.map( 42 | ok: (data) { 43 | _ref.read(todoTasksFamily(userId).notifier).insert(data.value); 44 | _ref.read(routerProvider).goNamed_( 45 | Routes.home, 46 | params: { 47 | ParamKeys.tab: HomeTab.task.value, 48 | }, 49 | ); 50 | }, 51 | err: (e) { 52 | // TODO(torikatsu): handle error. 53 | }, 54 | ); 55 | }, 56 | ); 57 | } 58 | 59 | @override 60 | void dispose() { 61 | state.dispose(); 62 | super.dispose(); 63 | } 64 | } 65 | 66 | @freezed 67 | class _CreateTaskState with _$_CreateTaskState { 68 | factory _CreateTaskState({ 69 | required FormModel name, 70 | }) = __CreateTaskController; 71 | 72 | _CreateTaskState._(); 73 | 74 | late final isValidAll = name.isValid; 75 | 76 | _CreateTaskState onSubmit() => copyWith(name: name.onSubmit()); 77 | 78 | void dispose() => name.dispose(); 79 | } 80 | -------------------------------------------------------------------------------- /lib/view/page/debug_page/debug_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_todo/model/result.dart'; 3 | import 'package:flutter_todo/model/task.dart'; 4 | import 'package:flutter_todo/provider/local/local_provider.dart'; 5 | import 'package:flutter_todo/provider/model/task_repository_provider.dart'; 6 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 7 | 8 | class DebugPage extends ConsumerWidget { 9 | const DebugPage({super.key}); 10 | 11 | @override 12 | Widget build(context, ref) { 13 | return Scaffold( 14 | appBar: AppBar( 15 | title: const Text('debug'), 16 | ), 17 | body: Center( 18 | child: Column( 19 | children: [ 20 | ListTile( 21 | onTap: () async { 22 | final userId = ref.read(localUserProvider).userId; 23 | final result = >[]; 24 | for (var i = 0; i < 100; i++) { 25 | result.add(await ref 26 | .read(taskRepositoryFamily(userId)) 27 | .insert(name: '$i')); 28 | } 29 | }, 30 | title: const Text('create many todo'), 31 | ), 32 | ListTile( 33 | onTap: () async { 34 | final userId = ref.read(localUserProvider).userId; 35 | final result = []; 36 | for (var i = 0; i < 100; i++) { 37 | result.add(await ref 38 | .read(taskRepositoryFamily(userId)) 39 | .insert(name: '$i')); 40 | } 41 | await Future.wait(result.map( 42 | (e) => ref 43 | .read(taskRepositoryFamily(userId)) 44 | .update(e.copyWith(isDone: true)), 45 | )); 46 | }, 47 | title: const Text('create many done'), 48 | ), 49 | ], 50 | ), 51 | ), 52 | ); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /lib/view/page/edit_task_page/edit_task_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_todo/model/task.dart'; 3 | import 'package:flutter_todo/provider/local/local_provider.dart'; 4 | import 'package:flutter_todo/util/tupple.dart'; 5 | import 'package:flutter_todo/view/component/error_view.dart'; 6 | import 'package:flutter_todo/view/component/loading_view.dart'; 7 | import 'package:flutter_todo/view/component/my_form.dart'; 8 | import 'package:flutter_todo/view/component/not_found_view.dart'; 9 | import 'package:flutter_todo/view/page/edit_task_page/edit_task_page_controller.dart'; 10 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 11 | import 'package:flutter_todo/util/async_value.dart'; 12 | 13 | class EditTaskPage extends ConsumerWidget { 14 | const EditTaskPage({super.key}); 15 | 16 | @override 17 | Widget build(context, ref) { 18 | final taskId = ref.watch(localTaskIdParamProvier); 19 | final userId = ref.watch(localUserProvider).userId; 20 | final state = ref.watch(prepareTaskEditProvider(T2(userId, taskId))); 21 | return Scaffold( 22 | appBar: AppBar( 23 | title: Text(taskId.value), 24 | ), 25 | body: state.flatMap( 26 | data: _EditView.new, 27 | loading: LoadingView.new, 28 | notFound: NotFoundView.new, 29 | error: ErrorView.new, 30 | ), 31 | ); 32 | } 33 | } 34 | 35 | class _EditView extends ConsumerWidget { 36 | const _EditView(this.task); 37 | 38 | final Task task; 39 | 40 | @override 41 | Widget build(BuildContext context, WidgetRef ref) { 42 | final userId = ref.watch(localUserProvider).userId; 43 | final state = ref.watch(taskEditControllerFamily(T2(userId, task))); 44 | final controller = 45 | ref.read(taskEditControllerFamily(T2(userId, task)).notifier); 46 | return Column( 47 | children: [ 48 | MyTextField( 49 | model: state.name, 50 | decoration: const InputDecoration( 51 | hintText: 'task name', 52 | helperText: 'task name', 53 | label: Text('task name'), 54 | ), 55 | ), 56 | ElevatedButton( 57 | onPressed: controller.onSubmit, 58 | child: const Text('submit'), 59 | ), 60 | ], 61 | ); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /lib/view/page/edit_task_page/edit_task_page_controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_todo/infrastructure/firestore_error.dart'; 2 | import 'package:flutter_todo/model/result.dart'; 3 | import 'package:flutter_todo/model/user.dart'; 4 | import 'package:flutter_todo/provider/route/pram.dart'; 5 | import 'package:flutter_todo/provider/route/router_provider.dart'; 6 | import 'package:flutter_todo/provider/route/routes.dart'; 7 | import 'package:flutter_todo/util/tupple.dart'; 8 | import 'package:freezed_annotation/freezed_annotation.dart'; 9 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 10 | 11 | import 'package:flutter_todo/model/form_model.dart'; 12 | import 'package:flutter_todo/model/task.dart'; 13 | import 'package:flutter_todo/model/validator.dart'; 14 | import 'package:flutter_todo/provider/global_controller/loading_provider.dart'; 15 | import 'package:flutter_todo/provider/model/task_provider.dart'; 16 | import 'package:flutter_todo/provider/model/task_repository_provider.dart'; 17 | 18 | part '../../../generated/view/page/edit_task_page/edit_task_page_controller.freezed.dart'; 19 | 20 | final prepareTaskEditProvider = Provider.autoDispose 21 | .family>, T2>( 22 | (ref, args) => ref.watch(taskFamily(args)), 23 | ); 24 | 25 | final taskEditControllerFamily = StateNotifierProvider.autoDispose 26 | .family>( 27 | TaskEditController.new, 28 | ); 29 | 30 | class TaskEditController extends StateNotifier<_TaskEditState> { 31 | TaskEditController(this._ref, T2 args) 32 | : userId = args.v1, 33 | super( 34 | _TaskEditState( 35 | initTask: args.v2, 36 | name: createFormModel(mandatoryValidator, args.v2.name), 37 | ), 38 | ) { 39 | state.name.initialize(_onChangeName); 40 | } 41 | 42 | final Ref _ref; 43 | final UserId userId; 44 | 45 | _onChangeName(FormModel name) => state = state.copyWith(name: name); 46 | 47 | bool _isTarget(Task item) => item.id == state.initTask.id; 48 | 49 | Future onSubmit() async { 50 | state = state.copyWith(name: state.name.onSubmit()); 51 | if (!state.isValidAll) { 52 | return; 53 | } 54 | final updatedTask = state.initTask.updateName(state.name.text); 55 | await _ref.read(loadingProvider.notifier).run( 56 | () async { 57 | await _ref.read(taskRepositoryFamily(userId)).update(updatedTask); 58 | updatedTask.isDone 59 | ? _ref 60 | .read(doneTasksFamily(userId).notifier) 61 | .update(_isTarget, updatedTask) 62 | : _ref 63 | .read(todoTasksFamily(userId).notifier) 64 | .update(_isTarget, updatedTask); 65 | _ref.refresh( 66 | taskFamily( 67 | T2( 68 | userId, 69 | state.initTask.id, 70 | ), 71 | ), 72 | ); 73 | _ref.read(routerProvider).goNamed_( 74 | Routes.taskDetail, 75 | params: { 76 | ParamKeys.tab: HomeTab.task.value, 77 | ParamKeys.taskId: state.initTask.id.value, 78 | }, 79 | ); 80 | }, 81 | ); 82 | } 83 | 84 | @override 85 | void dispose() { 86 | state.dispose(); 87 | super.dispose(); 88 | } 89 | } 90 | 91 | @freezed 92 | class _TaskEditState with _$_TaskEditState { 93 | factory _TaskEditState({ 94 | required Task initTask, 95 | required FormModel name, 96 | }) = __TaskEditState; 97 | 98 | _TaskEditState._(); 99 | 100 | late final isValidAll = name.isValid; 101 | 102 | void dispose() => name.dispose(); 103 | } 104 | -------------------------------------------------------------------------------- /lib/view/page/error_page/error_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_todo/view/page/error_page/error_page_controller.dart'; 3 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 4 | 5 | class ErrorPage extends ConsumerWidget { 6 | const ErrorPage({super.key}); 7 | 8 | @override 9 | Widget build(BuildContext context, ref) { 10 | final controller = ref.read(errorController); 11 | return Scaffold( 12 | appBar: AppBar( 13 | title: const Text('error'), 14 | ), 15 | body: Center( 16 | child: TextButton( 17 | onPressed: controller.backHome, 18 | child: const Text('back to home'), 19 | ), 20 | ), 21 | ); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /lib/view/page/error_page/error_page_controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_todo/provider/route/pram.dart'; 2 | import 'package:flutter_todo/provider/route/router_provider.dart'; 3 | import 'package:flutter_todo/provider/route/routes.dart'; 4 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 5 | 6 | final errorController = Provider(ErrorController.new); 7 | 8 | class ErrorController { 9 | ErrorController(this._ref); 10 | final Ref _ref; 11 | 12 | void backHome() => _ref.read(routerProvider).goNamed_( 13 | Routes.home, 14 | params: { 15 | ParamKeys.tab: HomeTab.task.value, 16 | }, 17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /lib/view/page/home_page/home_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_todo/provider/local/local_provider.dart'; 3 | import 'package:flutter_todo/view/component/lazy_indexed_stack.dart'; 4 | import 'package:flutter_todo/view/page/home_page/home_page_controller.dart'; 5 | import 'package:flutter_todo/view/screen/mypage_screen/mypage_screen.dart'; 6 | import 'package:flutter_todo/view/screen/task_screen/task_screen.dart'; 7 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 8 | 9 | class HomePage extends ConsumerWidget { 10 | const HomePage({super.key}); 11 | 12 | @override 13 | Widget build(context, ref) { 14 | final tab = ref.watch(localTabParamProvier); 15 | final controller = ref.read(homeControllerProvider.notifier); 16 | return Scaffold( 17 | body: LazyIndexedStack( 18 | builders: [ 19 | (context) => const TaskScreen(), 20 | (context) => const MyPageScreen(), 21 | ], 22 | index: tab.index, 23 | ), 24 | bottomNavigationBar: BottomNavigationBar( 25 | items: const [ 26 | BottomNavigationBarItem(icon: Icon(Icons.task), label: 'task'), 27 | BottomNavigationBarItem(icon: Icon(Icons.person), label: 'mypage'), 28 | ], 29 | currentIndex: tab.index, 30 | onTap: controller.onTapTab, 31 | ), 32 | ); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /lib/view/page/home_page/home_page_controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_todo/provider/route/pram.dart'; 2 | import 'package:flutter_todo/provider/route/router_provider.dart'; 3 | import 'package:flutter_todo/provider/route/routes.dart'; 4 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 5 | 6 | final homeControllerProvider = 7 | StateNotifierProvider.autoDispose( 8 | HomeController.new, 9 | ); 10 | 11 | class HomeController extends StateNotifier { 12 | HomeController(this._ref) : super(null); 13 | 14 | final Ref _ref; 15 | 16 | void onTapTab(int index) => _ref.read(routerProvider).goNamed_( 17 | Routes.home, 18 | params: { 19 | ParamKeys.tab: HomeTab.fromIndex(index).value, 20 | }, 21 | ); 22 | } 23 | -------------------------------------------------------------------------------- /lib/view/page/notfound_page/not_found_page_controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_todo/provider/route/pram.dart'; 2 | import 'package:flutter_todo/provider/route/router_provider.dart'; 3 | import 'package:flutter_todo/provider/route/routes.dart'; 4 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 5 | 6 | final notFoundController = Provider(NotFoundController.new); 7 | 8 | class NotFoundController { 9 | NotFoundController(this._ref); 10 | final Ref _ref; 11 | 12 | void backHome() => _ref.read(routerProvider).goNamed_( 13 | Routes.home, 14 | params: { 15 | ParamKeys.tab: HomeTab.task.value, 16 | }, 17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /lib/view/page/notfound_page/notfound_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_todo/view/page/notfound_page/not_found_page_controller.dart'; 3 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 4 | 5 | class NotFoundPage extends ConsumerWidget { 6 | const NotFoundPage({super.key}); 7 | 8 | @override 9 | Widget build(BuildContext context, ref) { 10 | final controller = ref.read(notFoundController); 11 | return Scaffold( 12 | appBar: AppBar( 13 | title: const Text('not found'), 14 | ), 15 | body: Center( 16 | child: TextButton( 17 | onPressed: controller.backHome, 18 | child: const Text('back to home'), 19 | ), 20 | ), 21 | ); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /lib/view/page/register_page/register_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_todo/model/user.dart'; 3 | import 'package:flutter_todo/provider/local/local_provider.dart'; 4 | import 'package:flutter_todo/view/component/my_form.dart'; 5 | import 'package:flutter_todo/view/page/register_page/register_page_controller.dart'; 6 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 7 | 8 | class RegisterPage extends ConsumerWidget { 9 | const RegisterPage({super.key}); 10 | 11 | @override 12 | Widget build(BuildContext context, ref) { 13 | final userId = UserId.fromAuthId(ref.read(localAuthProvider).userId); 14 | final state = ref.watch( 15 | registerPageControllerProvider(userId), 16 | ); 17 | final controller = 18 | ref.read(registerPageControllerProvider(userId).notifier); 19 | return Scaffold( 20 | appBar: AppBar( 21 | title: const Text('register page'), 22 | ), 23 | body: Column( 24 | children: [ 25 | MyTextField( 26 | model: state.username, 27 | decoration: const InputDecoration( 28 | hintText: 'username', 29 | helperText: 'username', 30 | label: Text('username'), 31 | ), 32 | ), 33 | ElevatedButton( 34 | onPressed: controller.submit, 35 | child: const Text('submit and register'), 36 | ), 37 | ], 38 | ), 39 | ); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /lib/view/page/register_page/register_page_controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_todo/infrastructure/firestore_error.dart'; 2 | import 'package:flutter_todo/model/form_model.dart'; 3 | import 'package:flutter_todo/model/user.dart'; 4 | import 'package:flutter_todo/model/validator.dart'; 5 | import 'package:flutter_todo/provider/global_controller/loading_provider.dart'; 6 | import 'package:flutter_todo/provider/infrastructure/user_provider.dart'; 7 | import 'package:flutter_todo/provider/model/user_repository_provider.dart'; 8 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 9 | import 'package:freezed_annotation/freezed_annotation.dart'; 10 | 11 | part '../../../generated/view/page/register_page/register_page_controller.freezed.dart'; 12 | 13 | final registerPageControllerProvider = StateNotifierProvider.autoDispose 14 | .family( 15 | RegisterPageController.new, 16 | ); 17 | 18 | class RegisterPageController extends StateNotifier { 19 | RegisterPageController(this._ref, this._userid) 20 | : super( 21 | RegisterPageState( 22 | username: createFormModel( 23 | mandatoryValidator, 24 | ), 25 | ), 26 | ) { 27 | state.username.initialize(_onChangeUsername); 28 | } 29 | 30 | final Ref _ref; 31 | final UserId _userid; 32 | 33 | _onChangeUsername(FormModel username) => 34 | state = state.copyWith(username: username); 35 | 36 | Future submit() async { 37 | state = state.onSubmit(); 38 | if (!state.isValidAll) { 39 | return; 40 | } 41 | final result = await _ref.read(userRepositoryProvider).register( 42 | id: _userid, 43 | username: state.username.text, 44 | ); 45 | 46 | await _ref.read(loadingProvider.notifier).run( 47 | () async { 48 | await result.when( 49 | ok: (_) async { 50 | // refresh user and wait for update of user 51 | _ref.refresh(authProvider); 52 | await _ref.read(userProvider.stream).first; 53 | }, 54 | err: (e) async { 55 | switch (e) { 56 | case FirestoreError.error: 57 | state = state.copyWith( 58 | username: state.username.addServerError('error has occured'), 59 | ); 60 | break; 61 | case FirestoreError.notFound: 62 | state = state.copyWith( 63 | username: state.username.addServerError('error has occured'), 64 | ); 65 | break; 66 | } 67 | }, 68 | ); 69 | }, 70 | ); 71 | } 72 | 73 | @override 74 | void dispose() { 75 | state.dispose(); 76 | super.dispose(); 77 | } 78 | } 79 | 80 | @freezed 81 | class RegisterPageState with _$RegisterPageState { 82 | factory RegisterPageState({ 83 | required FormModel username, 84 | }) = _RegisterPageState; 85 | 86 | RegisterPageState._(); 87 | 88 | RegisterPageState onSubmit() => copyWith( 89 | username: username.onSubmit(), 90 | ); 91 | 92 | late final bool isValidAll = username.isValid; 93 | 94 | void dispose() => username.dispose(); 95 | } 96 | -------------------------------------------------------------------------------- /lib/view/page/setting_page/setting_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_todo/provider/local/local_provider.dart'; 3 | import 'package:flutter_todo/view/component/my_form.dart'; 4 | import 'package:flutter_todo/view/page/setting_page/setting_page_controller.dart'; 5 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 6 | 7 | class SettingPage extends ConsumerWidget { 8 | const SettingPage({super.key}); 9 | 10 | @override 11 | Widget build(BuildContext context, WidgetRef ref) { 12 | final user = ref.watch(localUserProvider); 13 | final state = ref.watch(settingPageControllerProvider(user)); 14 | 15 | return Scaffold( 16 | appBar: AppBar( 17 | title: const Text('setting'), 18 | ), 19 | body: Column( 20 | children: [ 21 | MyTextField( 22 | model: state.username, 23 | decoration: const InputDecoration( 24 | hintText: 'enter new username', 25 | helperText: 'enter new username', 26 | label: Text('username'), 27 | ), 28 | ), 29 | ElevatedButton( 30 | onPressed: 31 | ref.read(settingPageControllerProvider(user).notifier).onSubmit, 32 | child: const Text('submit'), 33 | ) 34 | ], 35 | ), 36 | ); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /lib/view/page/setting_page/setting_page_controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_todo/infrastructure/firestore_error.dart'; 2 | import 'package:flutter_todo/model/form_model.dart'; 3 | import 'package:flutter_todo/model/user.dart'; 4 | import 'package:flutter_todo/model/validator.dart'; 5 | import 'package:flutter_todo/provider/global_controller/loading_provider.dart'; 6 | import 'package:flutter_todo/provider/model/user_repository_provider.dart'; 7 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 8 | import 'package:freezed_annotation/freezed_annotation.dart'; 9 | 10 | part '../../../generated/view/page/setting_page/setting_page_controller.freezed.dart'; 11 | 12 | final settingPageControllerProvider = 13 | StateNotifierProvider.family( 14 | SettingPageController.new, 15 | ); 16 | 17 | class SettingPageController extends StateNotifier { 18 | SettingPageController(this.ref, User initUser) 19 | : super( 20 | SettingPageState( 21 | initUser: initUser, 22 | username: createFormModel(mandatoryValidator), 23 | ), 24 | ) { 25 | state.username.initialize(_onChangeUsername); 26 | } 27 | 28 | final Ref ref; 29 | 30 | _onChangeUsername(FormModel username) => 31 | state = state.copyWith(username: username); 32 | 33 | void onSubmit() async { 34 | state = state.onSubmit(); 35 | if (!state.isValidAll) { 36 | return; 37 | } 38 | 39 | final nextUser = state.initUser.copyWith(username: state.username.text); 40 | ref.read(loadingProvider.notifier).run( 41 | () async { 42 | final result = await ref.read(userRepositoryProvider).update(nextUser); 43 | 44 | // TODO(torikatsu): handle result 45 | result.when( 46 | ok: (_) {}, 47 | err: (e) { 48 | switch (e) { 49 | case FirestoreError.error: 50 | return; 51 | case FirestoreError.notFound: 52 | return; 53 | default: 54 | return; 55 | } 56 | }, 57 | ); 58 | }, 59 | ); 60 | } 61 | 62 | @override 63 | void dispose() { 64 | state.dispose(); 65 | super.dispose(); 66 | } 67 | } 68 | 69 | @freezed 70 | class SettingPageState with _$SettingPageState { 71 | factory SettingPageState({ 72 | required User initUser, 73 | required FormModel username, 74 | }) = _SettingPageState; 75 | 76 | SettingPageState._(); 77 | 78 | late final isValidAll = username.isValid; 79 | 80 | SettingPageState onSubmit() => copyWith(username: username.onSubmit()); 81 | 82 | void dispose() => username.dispose(); 83 | } 84 | -------------------------------------------------------------------------------- /lib/view/page/signin_page/signin_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_todo/view/component/my_form.dart'; 3 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 4 | 5 | import 'signin_page_controller.dart'; 6 | 7 | class SigninPage extends ConsumerWidget { 8 | const SigninPage({super.key}); 9 | 10 | @override 11 | Widget build(context, ref) { 12 | final state = ref.watch(signinControllerProvider); 13 | final controller = ref.read(signinControllerProvider.notifier); 14 | 15 | return Scaffold( 16 | appBar: AppBar( 17 | title: const Text('signin'), 18 | ), 19 | body: Column( 20 | children: [ 21 | TextButton( 22 | onPressed: controller.toSignup, 23 | child: const Text('to signup'), 24 | ), 25 | MyTextField( 26 | model: state.email, 27 | decoration: const InputDecoration( 28 | hintText: 'xxxx@example.com', 29 | helperText: 'xxxx@example.com', 30 | label: Text('email'), 31 | ), 32 | ), 33 | MyTextField( 34 | model: state.password, 35 | decoration: const InputDecoration( 36 | hintText: 'more than 8 characters', 37 | helperText: 'more than 8 characters', 38 | label: Text('password'), 39 | ), 40 | ), 41 | ElevatedButton( 42 | onPressed: controller.submit, 43 | child: const Text('signin'), 44 | ), 45 | ], 46 | ), 47 | ); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /lib/view/page/signin_page/signin_page_controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_todo/model/form_model.dart'; 2 | import 'package:flutter_todo/model/validator.dart'; 3 | import 'package:flutter_todo/infrastructure/authenticator_provider.dart'; 4 | import 'package:flutter_todo/provider/global_controller/loading_provider.dart'; 5 | import 'package:flutter_todo/provider/global_controller/network_dialog_provider.dart'; 6 | import 'package:flutter_todo/provider/route/router_provider.dart'; 7 | import 'package:flutter_todo/provider/route/routes.dart'; 8 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 9 | import 'package:freezed_annotation/freezed_annotation.dart'; 10 | 11 | part '../../../generated/view/page/signin_page/signin_page_controller.freezed.dart'; 12 | 13 | final signinControllerProvider = 14 | StateNotifierProvider.autoDispose( 15 | SigninNotifier.new, 16 | ); 17 | 18 | class SigninNotifier extends StateNotifier<_SigninState> { 19 | SigninNotifier(this._ref) 20 | : super( 21 | _SigninState( 22 | email: createFormModel(emailValidator), 23 | password: createFormModel(passwordValidator), 24 | ), 25 | ) { 26 | state.email.initialize(_onChangeEmail); 27 | state.password.initialize(_onChangePassword); 28 | } 29 | 30 | final Ref _ref; 31 | 32 | _onChangeEmail(FormModel email) => state = state.copyWith(email: email); 33 | 34 | _onChangePassword(FormModel password) => 35 | state = state.copyWith(password: password); 36 | 37 | void toSignup() => _ref.read(routerProvider).goNamed_(Routes.signUp); 38 | 39 | Future submit() async { 40 | state = state.onSubmit(); 41 | if (!state.isValidAll) { 42 | return; 43 | } 44 | _ref.read(loadingProvider.notifier).run( 45 | () async { 46 | final result = await _ref.read(authenticatorProvider).signin( 47 | email: state.email.text, 48 | password: state.password.text, 49 | ); 50 | 51 | result.when( 52 | ok: (_) {}, 53 | err: (e) { 54 | switch (e) { 55 | case SigninError.userDisabled: 56 | state = state.copyWith( 57 | email: state.email.addServerError('アカウントが無効です')); 58 | break; 59 | case SigninError.userNotFound: 60 | state = state.copyWith( 61 | email: state.email.addServerError('アカウントが存在しません')); 62 | break; 63 | case SigninError.wrongPassword: 64 | state = state.copyWith( 65 | password: state.password.addServerError('パスワードが違います')); 66 | break; 67 | case SigninError.network: 68 | _ref.read(networkDialogProvider.notifier).show(); 69 | break; 70 | } 71 | }, 72 | ); 73 | }, 74 | ); 75 | } 76 | 77 | @override 78 | void dispose() { 79 | state.dispose(); 80 | super.dispose(); 81 | } 82 | } 83 | 84 | @freezed 85 | class _SigninState with _$_SigninState { 86 | factory _SigninState({ 87 | required FormModel email, 88 | required FormModel password, 89 | }) = __SigninInputState; 90 | 91 | _SigninState._(); 92 | 93 | late final isValidAll = email.isValid && password.isValid; 94 | 95 | _SigninState onSubmit() => copyWith( 96 | email: email.onSubmit(), 97 | password: password.onSubmit(), 98 | ); 99 | 100 | void dispose() { 101 | email.dispose(); 102 | password.dispose(); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /lib/view/page/signup_page/signup_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_todo/view/component/my_form.dart'; 3 | import 'package:flutter_todo/view/page/signup_page/signup_page_controller.dart'; 4 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 5 | 6 | class SignupPage extends ConsumerWidget { 7 | const SignupPage({super.key}); 8 | 9 | @override 10 | Widget build(context, ref) { 11 | final state = ref.watch(signupControllerProvider); 12 | final controller = ref.read(signupControllerProvider.notifier); 13 | 14 | return Scaffold( 15 | appBar: AppBar( 16 | title: const Text('signup'), 17 | ), 18 | body: Column( 19 | children: [ 20 | TextButton( 21 | onPressed: controller.toSignin, 22 | child: const Text('to signin'), 23 | ), 24 | MyTextField( 25 | model: state.email, 26 | decoration: const InputDecoration( 27 | hintText: 'xxxx@example.com', 28 | helperText: 'xxxx@example.com', 29 | label: Text('email'), 30 | ), 31 | ), 32 | MyTextField( 33 | model: state.password, 34 | decoration: const InputDecoration( 35 | hintText: 'more than 8 characters', 36 | helperText: 'more than 8 characters', 37 | label: Text('password'), 38 | ), 39 | ), 40 | MyTextField( 41 | model: state.confirmPassword, 42 | decoration: const InputDecoration( 43 | label: Text('confirm password'), 44 | ), 45 | ), 46 | ElevatedButton( 47 | onPressed: controller.submit, 48 | child: const Text('submit'), 49 | ), 50 | ], 51 | ), 52 | ); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /lib/view/page/signup_page/signup_page_controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_todo/model/form_model.dart'; 2 | import 'package:flutter_todo/model/validator.dart'; 3 | import 'package:flutter_todo/infrastructure/authenticator_provider.dart'; 4 | import 'package:flutter_todo/provider/global_controller/loading_provider.dart'; 5 | import 'package:flutter_todo/provider/global_controller/network_dialog_provider.dart'; 6 | import 'package:flutter_todo/provider/route/router_provider.dart'; 7 | import 'package:flutter_todo/provider/route/routes.dart'; 8 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 9 | import 'package:freezed_annotation/freezed_annotation.dart'; 10 | 11 | part '../../../generated/view/page/signup_page/signup_page_controller.freezed.dart'; 12 | 13 | final signupControllerProvider = 14 | StateNotifierProvider.autoDispose( 15 | SignupController.new); 16 | 17 | class SignupController extends StateNotifier<_SignupState> { 18 | SignupController(this._ref) 19 | : super( 20 | _SignupState( 21 | email: createFormModel(emailValidator), 22 | password: createFormModel(passwordValidator), 23 | confirmPassword: createFormModel(mandatoryValidator), 24 | ), 25 | ) { 26 | state.email.initialize(_onChangeEmail); 27 | state.password.initialize(_onChangePassword); 28 | state.confirmPassword.initialize(_onChangeConfirmPassword); 29 | } 30 | 31 | void _onChangeEmail(FormModel email) { 32 | state = state.copyWith(email: email); 33 | } 34 | 35 | void _onChangePassword(FormModel password) { 36 | state = state.copyWith( 37 | password: password, 38 | confirmPassword: _validateConfirmPassword( 39 | password, 40 | state.confirmPassword, 41 | ), 42 | ); 43 | } 44 | 45 | void _onChangeConfirmPassword(FormModel confirmPassword) { 46 | state = state.copyWith( 47 | confirmPassword: _validateConfirmPassword( 48 | state.password, 49 | confirmPassword, 50 | ), 51 | ); 52 | } 53 | 54 | final Ref _ref; 55 | 56 | void toSignin() => _ref.read(routerProvider).goNamed_(Routes.signIn); 57 | 58 | Future submit() async { 59 | state = state.onSubmit(); 60 | if (!state.isValidAll) { 61 | return; 62 | } 63 | return _ref.read(loadingProvider.notifier).run( 64 | () async { 65 | final result = await _ref.read(authenticatorProvider).signup( 66 | email: state.email.text, 67 | password: state.password.text, 68 | ); 69 | 70 | result.when( 71 | ok: (_) {}, 72 | err: (e) { 73 | switch (e) { 74 | case SignupError.emailAlreadyInUse: 75 | state = state.copyWith( 76 | email: state.email.addServerError('既に登録されたメールアドレスです')); 77 | break; 78 | case SignupError.network: 79 | _ref.read(networkDialogProvider.notifier).show(); 80 | break; 81 | } 82 | }, 83 | ); 84 | }, 85 | ); 86 | } 87 | 88 | FormModel _validateConfirmPassword( 89 | FormModel password, FormModel confirmPassword) { 90 | return confirmPassword.copyWith( 91 | additionalValidationError: 92 | confirmPasswordValidator(password.text, confirmPassword.text), 93 | ); 94 | } 95 | 96 | @override 97 | void dispose() { 98 | state.dispose(); 99 | super.dispose(); 100 | } 101 | } 102 | 103 | @freezed 104 | class _SignupState with _$_SignupState { 105 | factory _SignupState({ 106 | required FormModel email, 107 | required FormModel password, 108 | required FormModel confirmPassword, 109 | }) = __SignupInputState; 110 | 111 | _SignupState._(); 112 | 113 | late final isValidAll = 114 | email.isValid && password.isValid && confirmPassword.isValid; 115 | 116 | _SignupState onSubmit() => copyWith( 117 | email: email.onSubmit(), 118 | password: password.onSubmit(), 119 | confirmPassword: confirmPassword.onSubmit().copyWith( 120 | additionalValidationError: confirmPasswordValidator( 121 | password.text, 122 | confirmPassword.text, 123 | ), 124 | ), 125 | ); 126 | 127 | void dispose() { 128 | email.dispose(); 129 | password.dispose(); 130 | confirmPassword.dispose(); 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /lib/view/page/task_detail_page/task_detail_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_todo/model/task.dart'; 3 | import 'package:flutter_todo/provider/local/local_provider.dart'; 4 | import 'package:flutter_todo/util/tupple.dart'; 5 | import 'package:flutter_todo/view/component/error_view.dart'; 6 | import 'package:flutter_todo/view/component/loading_view.dart'; 7 | import 'package:flutter_todo/view/component/not_found_view.dart'; 8 | import 'package:flutter_todo/view/page/task_detail_page/task_detail_page_controller.dart'; 9 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 10 | import 'package:flutter_todo/util/async_value.dart'; 11 | 12 | class TaskDetailPage extends ConsumerWidget { 13 | const TaskDetailPage({super.key}); 14 | 15 | @override 16 | Widget build(context, ref) { 17 | final userId = ref.watch(localUserProvider).userId; 18 | final taskId = ref.watch(localTaskIdParamProvier); 19 | final state = ref.watch(taskDetailFamily(T2(userId, taskId))); 20 | return Scaffold( 21 | appBar: AppBar( 22 | title: Text(taskId.value), 23 | ), 24 | body: state.flatMap( 25 | data: TaskDetailView.new, 26 | loading: LoadingView.new, 27 | notFound: NotFoundView.new, 28 | error: ErrorView.new, 29 | ), 30 | ); 31 | } 32 | } 33 | 34 | class TaskDetailView extends ConsumerWidget { 35 | const TaskDetailView(this.task, {super.key}); 36 | 37 | final Task task; 38 | 39 | @override 40 | Widget build(BuildContext context, WidgetRef ref) { 41 | final userId = ref.watch(localUserProvider).userId; 42 | return Column( 43 | children: [ 44 | Text(task.id.value), 45 | Text(task.name), 46 | Text(task.createdAt.toIso8601String()), 47 | Text(task.isDone.toString()), 48 | TextButton( 49 | onPressed: ref 50 | .read( 51 | taskDetailFamily( 52 | T2( 53 | userId, 54 | task.id, 55 | ), 56 | ).notifier, 57 | ) 58 | .toEditPage, 59 | child: const Text('to edit'), 60 | ), 61 | ], 62 | ); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /lib/view/page/task_detail_page/task_detail_page_controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_todo/infrastructure/firestore_error.dart'; 2 | import 'package:flutter_todo/model/result.dart'; 3 | import 'package:flutter_todo/model/task.dart'; 4 | import 'package:flutter_todo/model/user.dart'; 5 | import 'package:flutter_todo/provider/model/task_provider.dart'; 6 | import 'package:flutter_todo/provider/route/pram.dart'; 7 | import 'package:flutter_todo/provider/route/router_provider.dart'; 8 | import 'package:flutter_todo/provider/route/routes.dart'; 9 | import 'package:flutter_todo/util/async_value.dart'; 10 | import 'package:flutter_todo/util/tupple.dart'; 11 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 12 | 13 | final taskDetailFamily = StateNotifierProvider.autoDispose.family< 14 | TaskDetailController, 15 | AsyncValue>, 16 | T2>( 17 | (ref, args) => TaskDetailController(args.v1, args.v2), 18 | ); 19 | 20 | class TaskDetailController extends AsyncNotifier> { 21 | TaskDetailController(this._userId, this._taskId) : super(); 22 | 23 | final UserId _userId; 24 | final TaskId _taskId; 25 | 26 | @override 27 | Future> build() async { 28 | return ref.watch(taskFamily(T2(_userId, _taskId)).future); 29 | } 30 | 31 | void toEditPage() => state.flatWhenData( 32 | (task) => ref.read(routerProvider).goNamed_( 33 | Routes.taskEdit, 34 | params: { 35 | ParamKeys.tab: HomeTab.task.value, 36 | ParamKeys.taskId: task.id.value, 37 | }, 38 | ), 39 | ); 40 | } 41 | 42 | -------------------------------------------------------------------------------- /lib/view/page/task_tab/done_tab.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_todo/model/task.dart'; 3 | import 'package:flutter_todo/provider/model/task_provider.dart'; 4 | import 'package:flutter_todo/provider/route/pram.dart'; 5 | import 'package:flutter_todo/provider/route/router_provider.dart'; 6 | import 'package:flutter_todo/provider/route/routes.dart'; 7 | import 'package:flutter_todo/view/component/task_list_item.dart'; 8 | import 'package:flutter_todo/view/page/task_tab/done_tab_controller.dart'; 9 | import 'package:flutter_todo/provider/local/local_provider.dart'; 10 | import 'package:flutter_todo/view/component/error_view.dart'; 11 | import 'package:flutter_todo/view/component/loading_view.dart'; 12 | import 'package:flutter_todo/util/async_value.dart'; 13 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 14 | 15 | class DoneTab extends ConsumerWidget { 16 | const DoneTab({super.key}); 17 | 18 | @override 19 | Widget build(context, ref) { 20 | final userId = ref.watch(localUserProvider).userId; 21 | final state = ref.watch(doneTabControllerFamily(userId)); 22 | final list = ref.watch(doneTasksFamily(userId)); 23 | 24 | return list.flatMap( 25 | data: (list) => RefreshIndicator( 26 | // TODO(torikatsu): apply web 27 | onRefresh: ref.read(doneTasksFamily(userId).notifier).refresh, 28 | child: CustomScrollView( 29 | physics: const AlwaysScrollableScrollPhysics(), 30 | controller: state, 31 | slivers: [ 32 | if (list.hasRefreshError) 33 | const SliverToBoxAdapter( 34 | child: SizedBox( 35 | height: 60, 36 | width: double.infinity, 37 | child: Center( 38 | child: Text('error has occured'), 39 | ), 40 | ), 41 | ), 42 | SliverList( 43 | delegate: SliverChildBuilderDelegate( 44 | (context, index) => _ListItem(list.list[index]), 45 | childCount: list.list.length, 46 | ), 47 | ), 48 | if (list.isMoreLoading) 49 | const SliverToBoxAdapter( 50 | child: SizedBox( 51 | height: 60, 52 | width: double.infinity, 53 | child: Center( 54 | child: CircularProgressIndicator(), 55 | ), 56 | ), 57 | ) 58 | else if (list.hasLoadMoreError) 59 | SliverToBoxAdapter( 60 | child: SizedBox( 61 | height: 60, 62 | width: double.infinity, 63 | child: Center( 64 | child: TextButton( 65 | onPressed: 66 | ref.read(doneTasksFamily(userId).notifier).loadMore, 67 | child: const Text('retry'), 68 | )), 69 | ), 70 | ) 71 | ], 72 | ), 73 | ), 74 | loading: LoadingView.new, 75 | orElse: ErrorView.new, 76 | ); 77 | } 78 | } 79 | 80 | class _ListItem extends ConsumerWidget { 81 | const _ListItem(this.task); 82 | 83 | final Task task; 84 | 85 | @override 86 | Widget build(BuildContext context, WidgetRef ref) { 87 | return TaskListItem( 88 | key: ValueKey(task.id), 89 | task: task, 90 | onTap: (task) { 91 | ref.read(routerProvider).goNamed_( 92 | Routes.taskDetail, 93 | params: { 94 | ParamKeys.tab: HomeTab.task.value, 95 | ParamKeys.taskId: task.id.value, 96 | }, 97 | ); 98 | }, 99 | ); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /lib/view/page/task_tab/done_tab_controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | import 'package:flutter_todo/model/user.dart'; 3 | import 'package:flutter_todo/provider/model/task_provider.dart'; 4 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 5 | 6 | final doneTabControllerFamily = 7 | StateNotifierProvider.family( 8 | DoneTabController.new, 9 | ); 10 | 11 | class DoneTabController extends StateNotifier { 12 | DoneTabController(this._ref, this._userId) : super(ScrollController()) { 13 | state.addListener(() { 14 | // TODO(torikatsu): fix conditions 15 | if (state.offset > state.position.maxScrollExtent - 100) { 16 | _ref.read(doneTasksFamily(_userId).notifier).loadMore(); 17 | } 18 | }); 19 | } 20 | 21 | final Ref _ref; 22 | final UserId _userId; 23 | 24 | @override 25 | void dispose() { 26 | state.dispose(); 27 | super.dispose(); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /lib/view/page/task_tab/todo_tab.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_todo/model/task.dart'; 3 | import 'package:flutter_todo/provider/global_controller/date_format_provider.dart'; 4 | import 'package:flutter_todo/provider/model/task_provider.dart'; 5 | import 'package:flutter_todo/provider/route/pram.dart'; 6 | import 'package:flutter_todo/provider/route/router_provider.dart'; 7 | import 'package:flutter_todo/provider/route/routes.dart'; 8 | import 'package:flutter_todo/view/component/task_list_item.dart'; 9 | import 'package:flutter_todo/view/page/task_tab/todo_tab_controller.dart'; 10 | import 'package:flutter_todo/provider/local/local_provider.dart'; 11 | import 'package:flutter_todo/view/component/error_view.dart'; 12 | import 'package:flutter_todo/view/component/loading_view.dart'; 13 | import 'package:flutter_todo/util/async_value.dart'; 14 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 15 | 16 | class TodoTab extends ConsumerWidget { 17 | const TodoTab({super.key}); 18 | 19 | @override 20 | Widget build(context, ref) { 21 | final userId = ref.watch(localUserProvider).userId; 22 | final list = ref.watch(todoTasksFamily(userId)); 23 | 24 | return list.maybeFlatMap( 25 | data: (list) => RefreshIndicator( 26 | // TODO(torikatsu): apply web 27 | onRefresh: ref.read(todoTasksFamily(userId).notifier).refresh, 28 | child: CustomScrollView( 29 | physics: const AlwaysScrollableScrollPhysics(), 30 | controller: ref.watch(todoTabControllerProvider(userId)), 31 | slivers: [ 32 | if (list.hasRefreshError) 33 | const SliverToBoxAdapter( 34 | child: SizedBox( 35 | height: 60, 36 | width: double.infinity, 37 | child: Center( 38 | child: Text('error has occured'), 39 | ), 40 | ), 41 | ), 42 | SliverList( 43 | delegate: SliverChildBuilderDelegate( 44 | childCount: list.list.length, 45 | (context, index) => _ListItem( 46 | list.list[index], 47 | ), 48 | ), 49 | ), 50 | if (list.isMoreLoading) 51 | const SliverToBoxAdapter( 52 | child: SizedBox( 53 | height: 60, 54 | width: double.infinity, 55 | child: Center( 56 | child: CircularProgressIndicator(), 57 | ), 58 | ), 59 | ) 60 | else if (list.hasLoadMoreError) 61 | SliverToBoxAdapter( 62 | child: SizedBox( 63 | height: 60, 64 | width: double.infinity, 65 | child: Center( 66 | child: TextButton( 67 | onPressed: 68 | ref.read(todoTasksFamily(userId).notifier).loadMore, 69 | child: const Text('retry'), 70 | ), 71 | ), 72 | ), 73 | ) 74 | ], 75 | ), 76 | ), 77 | loading: LoadingView.new, 78 | orElse: const ErrorView(), 79 | ); 80 | } 81 | } 82 | 83 | class _ListItem extends ConsumerWidget { 84 | const _ListItem(this.task); 85 | 86 | final Task task; 87 | 88 | @override 89 | Widget build(BuildContext context, WidgetRef ref) { 90 | return Dismissible( 91 | key: ValueKey(task.id), 92 | onDismissed: (_) { 93 | final userId = ref.read(localUserProvider).userId; 94 | ref 95 | .read(todoTabControllerProvider(userId).notifier) 96 | .onDismissedItem(task); 97 | }, 98 | child: TaskListItem( 99 | task: task, 100 | onTap: (task) { 101 | ref.read(routerProvider).goNamed_( 102 | Routes.taskDetail, 103 | params: { 104 | ParamKeys.tab: HomeTab.task.value, 105 | ParamKeys.taskId: task.id.value, 106 | }, 107 | ); 108 | }, 109 | ), 110 | ); 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /lib/view/page/task_tab/todo_tab_controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | import 'package:flutter_todo/model/task.dart'; 3 | import 'package:flutter_todo/model/user.dart'; 4 | import 'package:flutter_todo/provider/global_controller/loading_provider.dart'; 5 | import 'package:flutter_todo/provider/model/task_provider.dart'; 6 | import 'package:flutter_todo/provider/model/task_repository_provider.dart'; 7 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 8 | 9 | final todoTabControllerProvider = 10 | StateNotifierProvider.family( 11 | TodoTabController.new, 12 | ); 13 | 14 | class TodoTabController extends StateNotifier { 15 | TodoTabController(this._ref, this._userId) : super(ScrollController()) { 16 | state.addListener(() { 17 | // TODO(torikatsu): fix conditions 18 | if (state.offset > state.position.maxScrollExtent - 100) { 19 | _ref.read(todoTasksFamily(_userId).notifier).loadMore(); 20 | } 21 | }); 22 | } 23 | 24 | final Ref _ref; 25 | final UserId _userId; 26 | 27 | Future onDismissedItem(Task task) => 28 | _ref.read(loadingProvider.notifier).run( 29 | () async { 30 | await _ref.read(taskRepositoryFamily(_userId)).update(task.done()); 31 | _ref.read(todoTasksFamily(_userId).notifier).delete(task); 32 | _ref.read(doneTasksFamily(_userId).notifier).insert(task); 33 | }, 34 | ); 35 | 36 | @override 37 | void dispose() { 38 | state.dispose(); 39 | super.dispose(); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /lib/view/screen/mypage_screen/mypage_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/foundation.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_todo/model/user.dart'; 4 | import 'package:flutter_todo/provider/local/local_provider.dart'; 5 | import 'package:flutter_todo/view/screen/mypage_screen/mypage_screen_controller.dart'; 6 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 7 | 8 | class MyPageScreen extends ConsumerWidget { 9 | const MyPageScreen({super.key}); 10 | 11 | @override 12 | Widget build(context, ref) { 13 | final user = ref.watch(localUserProvider); 14 | final controller = ref.read(myPageControllerProvider); 15 | return Scaffold( 16 | body: CustomScrollView( 17 | slivers: [ 18 | SliverAppBar( 19 | title: Text(user.username), 20 | pinned: true, 21 | expandedHeight: 100, 22 | elevation: 2, 23 | ), 24 | SliverToBoxAdapter( 25 | child: Transform.translate( 26 | offset: Offset(0, -50), 27 | child: MyPageView(user: user), 28 | ), 29 | ), 30 | SliverToBoxAdapter( 31 | child: ListTile( 32 | leading: const Icon(Icons.settings), 33 | title: const Text('setting'), 34 | onTap: controller.openSetting, 35 | ), 36 | ), 37 | SliverToBoxAdapter( 38 | child: ListTile( 39 | leading: const Icon(Icons.badge), 40 | title: const Text('license'), 41 | onTap: () => showLicensePage(context: context), 42 | ), 43 | ), 44 | SliverToBoxAdapter( 45 | child: ListTile( 46 | leading: const Icon(Icons.logout), 47 | title: const Text('signout'), 48 | onTap: controller.signOut, 49 | textColor: Theme.of(context).colorScheme.error, 50 | iconColor: Theme.of(context).colorScheme.error, 51 | ), 52 | ), 53 | if (kDebugMode) 54 | SliverToBoxAdapter( 55 | child: ListTile( 56 | leading: const Icon(Icons.flutter_dash), 57 | title: const Text('debug'), 58 | onTap: controller.openDebugPage, 59 | ), 60 | ), 61 | ], 62 | ), 63 | ); 64 | } 65 | } 66 | 67 | class MyPageView extends StatelessWidget { 68 | const MyPageView({required this.user, super.key}); 69 | 70 | final User user; 71 | 72 | @override 73 | Widget build(BuildContext context) { 74 | return Padding( 75 | padding: const EdgeInsets.all(8.0), 76 | child: Row( 77 | mainAxisAlignment: MainAxisAlignment.start, 78 | crossAxisAlignment: CrossAxisAlignment.center, 79 | mainAxisSize: MainAxisSize.min, 80 | children: [ 81 | ClipOval( 82 | child: Image.network( 83 | 'https://avatars.githubusercontent.com/u/50566499?v=4', 84 | height: 80, 85 | ), 86 | ), 87 | const SizedBox( 88 | width: 8, 89 | ), 90 | Expanded( 91 | child: Column( 92 | crossAxisAlignment: CrossAxisAlignment.start, 93 | mainAxisSize: MainAxisSize.min, 94 | children: [ 95 | Text( 96 | user.username, 97 | style: Theme.of(context).textTheme.headline6, 98 | ), 99 | Text( 100 | '@${user.userId.value}', 101 | style: Theme.of(context).textTheme.caption, 102 | ), 103 | ], 104 | ), 105 | ), 106 | ], 107 | ), 108 | ); 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /lib/view/screen/mypage_screen/mypage_screen_controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_todo/infrastructure/authenticator_provider.dart'; 2 | import 'package:flutter_todo/provider/global_controller/loading_provider.dart'; 3 | import 'package:flutter_todo/provider/global_controller/network_dialog_provider.dart'; 4 | import 'package:flutter_todo/provider/route/pram.dart'; 5 | import 'package:flutter_todo/provider/route/router_provider.dart'; 6 | import 'package:flutter_todo/provider/route/routes.dart'; 7 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 8 | 9 | final myPageControllerProvider = Provider(MyPageController.new); 10 | 11 | class MyPageController { 12 | MyPageController(this._ref); 13 | 14 | final Ref _ref; 15 | 16 | void openSetting() => _ref.read(routerProvider).goNamed_( 17 | Routes.setting, 18 | params: { 19 | ParamKeys.tab: HomeTab.mypage.value, 20 | }, 21 | ); 22 | 23 | void openDebugPage() => _ref.read(routerProvider).goNamed_( 24 | Routes.debug, 25 | ); 26 | 27 | void signOut() async { 28 | await _ref.read(loadingProvider.notifier).run(() async { 29 | final result = await _ref.read(authenticatorProvider).signout(); 30 | result.maybeWhen( 31 | err: (e) { 32 | switch (e) { 33 | case SignOutError.network: 34 | _ref.read(networkDialogProvider.notifier).show(); 35 | } 36 | }, 37 | orElse: () {}, 38 | ); 39 | }); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /lib/view/screen/task_screen/task_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_todo/config/page_storage_keys.dart'; 3 | import 'package:flutter_todo/provider/local/local_provider.dart'; 4 | import 'package:flutter_todo/provider/route/pram.dart'; 5 | import 'package:flutter_todo/view/page/task_tab/done_tab.dart'; 6 | import 'package:flutter_todo/view/page/task_tab/todo_tab.dart'; 7 | import 'package:flutter_todo/view/screen/task_screen/task_screen_controller.dart'; 8 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 9 | 10 | class TaskScreen extends ConsumerStatefulWidget { 11 | const TaskScreen({super.key}); 12 | 13 | @override 14 | ConsumerState createState() => _TodoScreenState(); 15 | } 16 | 17 | class _TodoScreenState extends ConsumerState 18 | with TickerProviderStateMixin, AutomaticKeepAliveClientMixin { 19 | late final TabController _controller; 20 | 21 | @override 22 | void initState() { 23 | _controller = TabController( 24 | length: 2, 25 | vsync: this, 26 | initialIndex: ref.read(localInnerTabProvier).index, 27 | ); 28 | super.initState(); 29 | } 30 | 31 | @override 32 | Widget build(BuildContext context) { 33 | super.build(context); 34 | ref.listen( 35 | localInnerTabProvier, 36 | (prev, next) => _controller.index = next.index, 37 | ); 38 | final controller = ref.read(taskScreenControllerProvider); 39 | 40 | return Scaffold( 41 | appBar: AppBar( 42 | title: const Text('task'), 43 | bottom: TabBar( 44 | controller: _controller, 45 | onTap: controller.onTap, 46 | tabs: const [ 47 | Tab(text: 'todo', icon: Icon(Icons.check_box_outline_blank)), 48 | Tab(text: 'done', icon: Icon(Icons.check_box_outlined)), 49 | ], 50 | ), 51 | ), 52 | body: TabBarView( 53 | controller: _controller, 54 | children: [ 55 | TodoTab(key: PageStorageKies.todoTab.key), 56 | DoneTab(key: PageStorageKies.doneTab.key), 57 | ], 58 | ), 59 | floatingActionButton: FloatingActionButton( 60 | onPressed: controller.onTapFab, 61 | child: const Icon(Icons.add), 62 | ), 63 | ); 64 | } 65 | 66 | @override 67 | bool get wantKeepAlive => true; 68 | } 69 | -------------------------------------------------------------------------------- /lib/view/screen/task_screen/task_screen_controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_todo/model/task.dart'; 2 | import 'package:flutter_todo/provider/route/pram.dart'; 3 | import 'package:flutter_todo/provider/route/router_provider.dart'; 4 | import 'package:flutter_todo/provider/route/routes.dart'; 5 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 6 | 7 | final taskScreenControllerProvider = 8 | Provider(TaskScreenController.new); 9 | 10 | class TaskScreenController { 11 | TaskScreenController(this._ref); 12 | final Ref _ref; 13 | 14 | void onTap(int index) { 15 | _ref.read(routerProvider).goNamed_( 16 | Routes.home, 17 | params: { 18 | ParamKeys.tab: HomeTab.task.value, 19 | }, 20 | queryParams: { 21 | QueryParamKeys.innerTab: InnerTab.fromIndex(index).value, 22 | }, 23 | ); 24 | } 25 | 26 | void toDetailPage(TaskId id) => _ref.read(routerProvider).goNamed_( 27 | Routes.taskDetail, 28 | params: { 29 | ParamKeys.tab: HomeTab.task.value, 30 | ParamKeys.taskId: id.value, 31 | }, 32 | ); 33 | 34 | void onTapFab() { 35 | _ref.read(routerProvider).goNamed_( 36 | Routes.taskCreate, 37 | params: { 38 | ParamKeys.tab: HomeTab.task.value, 39 | }, 40 | ); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: flutter_todo 2 | description: A TODO app. riverpod + navigator2 + switch env 3 | version: 1.0.0+1 4 | 5 | environment: 6 | sdk: ">=2.17.0 <3.0.0" 7 | 8 | dependencies: 9 | flutter: 10 | sdk: flutter 11 | flutter_hooks: ^0.18.2+1 12 | hooks_riverpod: ^2.0.0 13 | freezed_annotation: ^2.0.3 14 | json_annotation: ^4.4.0 15 | go_router: ^4.2.7 16 | firebase_core: ^1.20.0 17 | firebase_auth: ^3.5.0 18 | cloud_firestore: ^3.4.0 19 | intl: ^0.17.0 20 | # TODO(torikatsu): configure podfile to speed up for building ios 21 | 22 | dev_dependencies: 23 | flutter_test: 24 | sdk: flutter 25 | flutter_lints: ^2.0.1 26 | freezed: ^2.0.3+1 27 | build_runner: ^2.1.7 28 | source_gen: ^1.2.1 29 | json_serializable: ^6.1.4 30 | 31 | flutter: 32 | uses-material-design: true 33 | -------------------------------------------------------------------------------- /web/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/torikatsupg/flutter_todo/a8286ca196bbb52ea4797d2273662e0665879af2/web/favicon.png -------------------------------------------------------------------------------- /web/icons/Icon-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/torikatsupg/flutter_todo/a8286ca196bbb52ea4797d2273662e0665879af2/web/icons/Icon-192.png -------------------------------------------------------------------------------- /web/icons/Icon-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/torikatsupg/flutter_todo/a8286ca196bbb52ea4797d2273662e0665879af2/web/icons/Icon-512.png -------------------------------------------------------------------------------- /web/icons/Icon-maskable-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/torikatsupg/flutter_todo/a8286ca196bbb52ea4797d2273662e0665879af2/web/icons/Icon-maskable-192.png -------------------------------------------------------------------------------- /web/icons/Icon-maskable-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/torikatsupg/flutter_todo/a8286ca196bbb52ea4797d2273662e0665879af2/web/icons/Icon-maskable-512.png -------------------------------------------------------------------------------- /web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | flutter_todo 33 | 34 | 35 | 36 | 39 | 103 | 104 | 109 | 110 | -------------------------------------------------------------------------------- /web/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "flutter_todo", 3 | "short_name": "flutter_todo", 4 | "start_url": ".", 5 | "display": "standalone", 6 | "background_color": "#0175C2", 7 | "theme_color": "#0175C2", 8 | "description": "A new Flutter project.", 9 | "orientation": "portrait-primary", 10 | "prefer_related_applications": false, 11 | "icons": [ 12 | { 13 | "src": "icons/Icon-192.png", 14 | "sizes": "192x192", 15 | "type": "image/png" 16 | }, 17 | { 18 | "src": "icons/Icon-512.png", 19 | "sizes": "512x512", 20 | "type": "image/png" 21 | }, 22 | { 23 | "src": "icons/Icon-maskable-192.png", 24 | "sizes": "192x192", 25 | "type": "image/png", 26 | "purpose": "maskable" 27 | }, 28 | { 29 | "src": "icons/Icon-maskable-512.png", 30 | "sizes": "512x512", 31 | "type": "image/png", 32 | "purpose": "maskable" 33 | } 34 | ] 35 | } 36 | --------------------------------------------------------------------------------