├── ios ├── Flutter │ ├── Debug.xcconfig │ ├── Release.xcconfig │ └── AppFrameworkInfo.plist ├── Runner │ ├── Runner-Bridging-Header.h │ ├── Assets.xcassets │ │ ├── LaunchImage.imageset │ │ │ ├── LaunchImage.png │ │ │ ├── LaunchImage@2x.png │ │ │ ├── LaunchImage@3x.png │ │ │ ├── README.md │ │ │ └── Contents.json │ │ └── AppIcon.appiconset │ │ │ ├── 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-1024x1024@1x.png │ │ │ ├── Icon-App-83.5x83.5@2x.png │ │ │ └── Contents.json │ ├── AppDelegate.swift │ ├── Base.lproj │ │ ├── Main.storyboard │ │ └── LaunchScreen.storyboard │ └── Info.plist ├── Runner.xcodeproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ ├── WorkspaceSettings.xcsettings │ │ │ └── IDEWorkspaceChecks.plist │ ├── xcshareddata │ │ └── xcschemes │ │ │ └── Runner.xcscheme │ └── project.pbxproj ├── Runner.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── WorkspaceSettings.xcsettings │ │ └── IDEWorkspaceChecks.plist └── .gitignore ├── fastlane └── metadata │ └── android │ ├── de │ └── short_description.txt │ └── en-US │ ├── short_description.txt │ ├── images │ ├── icon.png │ └── phoneScreenshots │ │ ├── 01.jpg │ │ ├── 02.jpg │ │ ├── 03.jpg │ │ ├── 04.jpg │ │ ├── 05.jpg │ │ └── 06.jpg │ └── full_description.txt ├── web ├── favicon.png ├── icons │ ├── Icon-192.png │ ├── Icon-512.png │ ├── Icon-maskable-192.png │ └── Icon-maskable-512.png ├── manifest.json └── index.html ├── android ├── gradle.properties ├── app │ ├── src │ │ ├── main │ │ │ ├── res │ │ │ │ ├── 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 │ │ │ │ ├── drawable │ │ │ │ │ └── launch_background.xml │ │ │ │ ├── drawable-v21 │ │ │ │ │ └── launch_background.xml │ │ │ │ ├── values │ │ │ │ │ └── styles.xml │ │ │ │ └── values-night │ │ │ │ │ └── styles.xml │ │ │ ├── kotlin │ │ │ │ └── com │ │ │ │ │ └── example │ │ │ │ │ └── flutter_riverpod_todo_app │ │ │ │ │ └── MainActivity.kt │ │ │ └── AndroidManifest.xml │ │ ├── debug │ │ │ └── AndroidManifest.xml │ │ └── profile │ │ │ └── AndroidManifest.xml │ └── build.gradle ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties ├── .gitignore ├── settings.gradle └── build.gradle ├── assets ├── images │ └── empty.png └── fonts │ └── rubik │ ├── Rubik-Bold.ttf │ ├── Rubik-Light.ttf │ ├── Rubik-Medium.ttf │ ├── Rubik-Regular.ttf │ └── Rubik-SemiBold.ttf ├── screenshots ├── Frame - 1.png ├── Frame - 2.png ├── Frame - 3.png ├── Frame - 4.png ├── Frame - 5.png └── Frame - 6.png ├── lib ├── providers │ ├── storage_provider.dart │ ├── current_todo_provider.dart │ ├── todo_filter_type_provider.dart │ ├── export_providers.dart │ ├── todo_count_provider.dart │ ├── theme_provider.dart │ ├── filtered_todos_list_provider.dart │ ├── shared_utility_provider.dart │ └── todo_list_provider.dart ├── utils │ ├── assets.dart │ ├── unique_keys.dart │ ├── constants.dart │ ├── hooks │ │ └── scroll_controller_hook.dart │ ├── storage.dart │ └── styles │ │ └── task_app_theme.dart ├── ui │ ├── widgets │ │ ├── app_title_with_transition.dart │ │ ├── app_title.dart │ │ ├── custom_button.dart │ │ ├── toolbar.dart │ │ ├── category_button.dart │ │ └── todo_item.dart │ └── pages │ │ ├── back_layer_page.dart │ │ ├── about_us_page.dart │ │ ├── backup_and_restore.dart │ │ ├── backup_list.dart │ │ ├── instructions_page.dart │ │ └── home_page.dart ├── models │ ├── list_of_todo_model.dart │ └── todo_model.dart └── main.dart ├── .metadata ├── .vscode └── launch.json ├── .gitignore ├── LICENSE ├── test └── widget_test.dart ├── pubspec.yaml ├── analysis_options.yaml ├── README.md └── pubspec.lock /ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /fastlane/metadata/android/de/short_description.txt: -------------------------------------------------------------------------------- 1 | elegante ToDo-App mit einigen erweiterten Features -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/short_description.txt: -------------------------------------------------------------------------------- 1 | an elegant todo app with some advanced features -------------------------------------------------------------------------------- /web/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAlphaApp/flutter_riverpod_todo_app/HEAD/web/favicon.png -------------------------------------------------------------------------------- /web/icons/Icon-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAlphaApp/flutter_riverpod_todo_app/HEAD/web/icons/Icon-192.png -------------------------------------------------------------------------------- /web/icons/Icon-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAlphaApp/flutter_riverpod_todo_app/HEAD/web/icons/Icon-512.png -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.useAndroidX=true 3 | android.enableJetifier=true 4 | -------------------------------------------------------------------------------- /assets/images/empty.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAlphaApp/flutter_riverpod_todo_app/HEAD/assets/images/empty.png -------------------------------------------------------------------------------- /screenshots/Frame - 1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAlphaApp/flutter_riverpod_todo_app/HEAD/screenshots/Frame - 1.png -------------------------------------------------------------------------------- /screenshots/Frame - 2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAlphaApp/flutter_riverpod_todo_app/HEAD/screenshots/Frame - 2.png -------------------------------------------------------------------------------- /screenshots/Frame - 3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAlphaApp/flutter_riverpod_todo_app/HEAD/screenshots/Frame - 3.png -------------------------------------------------------------------------------- /screenshots/Frame - 4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAlphaApp/flutter_riverpod_todo_app/HEAD/screenshots/Frame - 4.png -------------------------------------------------------------------------------- /screenshots/Frame - 5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAlphaApp/flutter_riverpod_todo_app/HEAD/screenshots/Frame - 5.png -------------------------------------------------------------------------------- /screenshots/Frame - 6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAlphaApp/flutter_riverpod_todo_app/HEAD/screenshots/Frame - 6.png -------------------------------------------------------------------------------- /web/icons/Icon-maskable-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAlphaApp/flutter_riverpod_todo_app/HEAD/web/icons/Icon-maskable-192.png -------------------------------------------------------------------------------- /web/icons/Icon-maskable-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAlphaApp/flutter_riverpod_todo_app/HEAD/web/icons/Icon-maskable-512.png -------------------------------------------------------------------------------- /assets/fonts/rubik/Rubik-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAlphaApp/flutter_riverpod_todo_app/HEAD/assets/fonts/rubik/Rubik-Bold.ttf -------------------------------------------------------------------------------- /assets/fonts/rubik/Rubik-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAlphaApp/flutter_riverpod_todo_app/HEAD/assets/fonts/rubik/Rubik-Light.ttf -------------------------------------------------------------------------------- /assets/fonts/rubik/Rubik-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAlphaApp/flutter_riverpod_todo_app/HEAD/assets/fonts/rubik/Rubik-Medium.ttf -------------------------------------------------------------------------------- /assets/fonts/rubik/Rubik-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAlphaApp/flutter_riverpod_todo_app/HEAD/assets/fonts/rubik/Rubik-Regular.ttf -------------------------------------------------------------------------------- /assets/fonts/rubik/Rubik-SemiBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAlphaApp/flutter_riverpod_todo_app/HEAD/assets/fonts/rubik/Rubik-SemiBold.ttf -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAlphaApp/flutter_riverpod_todo_app/HEAD/fastlane/metadata/android/en-US/images/icon.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAlphaApp/flutter_riverpod_todo_app/HEAD/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAlphaApp/flutter_riverpod_todo_app/HEAD/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAlphaApp/flutter_riverpod_todo_app/HEAD/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAlphaApp/flutter_riverpod_todo_app/HEAD/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAlphaApp/flutter_riverpod_todo_app/HEAD/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/01.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAlphaApp/flutter_riverpod_todo_app/HEAD/fastlane/metadata/android/en-US/images/phoneScreenshots/01.jpg -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/02.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAlphaApp/flutter_riverpod_todo_app/HEAD/fastlane/metadata/android/en-US/images/phoneScreenshots/02.jpg -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/03.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAlphaApp/flutter_riverpod_todo_app/HEAD/fastlane/metadata/android/en-US/images/phoneScreenshots/03.jpg -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/04.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAlphaApp/flutter_riverpod_todo_app/HEAD/fastlane/metadata/android/en-US/images/phoneScreenshots/04.jpg -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/05.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAlphaApp/flutter_riverpod_todo_app/HEAD/fastlane/metadata/android/en-US/images/phoneScreenshots/05.jpg -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/06.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAlphaApp/flutter_riverpod_todo_app/HEAD/fastlane/metadata/android/en-US/images/phoneScreenshots/06.jpg -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAlphaApp/flutter_riverpod_todo_app/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAlphaApp/flutter_riverpod_todo_app/HEAD/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/TheAlphaApp/flutter_riverpod_todo_app/HEAD/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/TheAlphaApp/flutter_riverpod_todo_app/HEAD/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/TheAlphaApp/flutter_riverpod_todo_app/HEAD/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/TheAlphaApp/flutter_riverpod_todo_app/HEAD/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/TheAlphaApp/flutter_riverpod_todo_app/HEAD/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/TheAlphaApp/flutter_riverpod_todo_app/HEAD/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/TheAlphaApp/flutter_riverpod_todo_app/HEAD/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/TheAlphaApp/flutter_riverpod_todo_app/HEAD/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/TheAlphaApp/flutter_riverpod_todo_app/HEAD/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/TheAlphaApp/flutter_riverpod_todo_app/HEAD/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/TheAlphaApp/flutter_riverpod_todo_app/HEAD/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/TheAlphaApp/flutter_riverpod_todo_app/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAlphaApp/flutter_riverpod_todo_app/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAlphaApp/flutter_riverpod_todo_app/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAlphaApp/flutter_riverpod_todo_app/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAlphaApp/flutter_riverpod_todo_app/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /lib/providers/storage_provider.dart: -------------------------------------------------------------------------------- 1 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 2 | 3 | import '/utils/storage.dart'; 4 | 5 | final storageProvider = Provider((ref) => throw UnimplementedError()); 6 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /lib/providers/current_todo_provider.dart: -------------------------------------------------------------------------------- 1 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 2 | 3 | import '../models/todo_model.dart'; 4 | 5 | final currentTodo = Provider((ref) => throw UnimplementedError()); 6 | -------------------------------------------------------------------------------- /android/app/src/main/kotlin/com/example/flutter_riverpod_todo_app/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.example.flutter_riverpod_todo_app 2 | 3 | import io.flutter.embedding.android.FlutterActivity 4 | 5 | class MainActivity: FlutterActivity() { 6 | } 7 | -------------------------------------------------------------------------------- /lib/utils/assets.dart: -------------------------------------------------------------------------------- 1 | /// A class that contains the paths to various assets used in the application. 2 | class Assets { 3 | /// The path to the image used when there are no tasks. 4 | static const String noTask = 'assets/images/empty.png'; 5 | } 6 | -------------------------------------------------------------------------------- /lib/utils/unique_keys.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | final addTodoKey = UniqueKey(); 4 | final activeFilterKey = UniqueKey(); 5 | final completedFilterKey = UniqueKey(); 6 | final allFilterKey = UniqueKey(); 7 | final pinnedFilterKey = UniqueKey(); 8 | -------------------------------------------------------------------------------- /lib/providers/todo_filter_type_provider.dart: -------------------------------------------------------------------------------- 1 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 2 | 3 | enum TodoFilterType { 4 | all, 5 | active, 6 | completed, 7 | pinned, 8 | } 9 | 10 | final todoFilterType = StateProvider((ref) => TodoFilterType.active); 11 | -------------------------------------------------------------------------------- /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-7.5-all.zip 7 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.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/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /lib/providers/export_providers.dart: -------------------------------------------------------------------------------- 1 | export 'current_todo_provider.dart'; 2 | export 'filtered_todos_list_provider.dart'; 3 | export 'shared_utility_provider.dart'; 4 | export 'storage_provider.dart'; 5 | export 'theme_provider.dart'; 6 | export 'todo_count_provider.dart'; 7 | export 'todo_filter_type_provider.dart'; 8 | export 'todo_list_provider.dart'; 9 | -------------------------------------------------------------------------------- /.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: 3595343e20a61ff16d14e8ecc25f364276bb1b8b 8 | channel: stable 9 | 10 | project_type: app 11 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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. -------------------------------------------------------------------------------- /android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/full_description.txt: -------------------------------------------------------------------------------- 1 |

This is a todo app with local backup and restore functionality. It has many features:

-------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /lib/utils/constants.dart: -------------------------------------------------------------------------------- 1 | const String appName = 'com.example.riverpod_flutter_todo_app'; 2 | const String fbUrl = 'https://www.facebook.com/App-Dexon-102102212273649'; 3 | const String githubUrl = 'https://www.github.com/TheAlphaApp'; 4 | const String mailUrl = 'mailto:appdexonofficial@gmail.com?subject="regarding $appName&body='; 5 | const String sharedPrefTodoListKey = 'SharedData'; 6 | const String sharedDarkModeKey = 'isDarkModeEnabled'; 7 | const String emptyJsonStringData = '{"data": []}'; 8 | -------------------------------------------------------------------------------- /lib/providers/todo_count_provider.dart: -------------------------------------------------------------------------------- 1 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 2 | 3 | import 'todo_list_provider.dart'; 4 | 5 | final uncompletedTodoCount = Provider( 6 | (ref) { 7 | return ref 8 | .watch(todoListProvider) 9 | .data 10 | .where((todo) => !todo.isCompleted) 11 | .length; 12 | }, 13 | ); 14 | final totalTodoCount = Provider( 15 | (ref) { 16 | return ref.watch(todoListProvider).data.length; 17 | }, 18 | ); 19 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-v21/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /lib/ui/widgets/app_title_with_transition.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'app_title.dart'; 4 | 5 | class AppTitleWithTransition extends AppBarTitle { 6 | const AppTitleWithTransition(this.titleAnimController, 7 | {final double? fontSize = 24, Key? key}) 8 | : super(key: key, fontSize: fontSize); 9 | final AnimationController titleAnimController; 10 | 11 | @override 12 | Widget build(BuildContext context) { 13 | return FadeTransition( 14 | opacity: titleAnimController, 15 | child: AppBarTitle( 16 | fontSize: fontSize, 17 | trailingTitle: 'Todopad', 18 | ), 19 | ); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.8.0' 3 | repositories { 4 | google() 5 | mavenCentral() 6 | } 7 | 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:7.0.2' 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 | project.evaluationDependsOn(':app') 25 | } 26 | 27 | tasks.register("clean", Delete) { 28 | delete rootProject.buildDir 29 | } 30 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /lib/providers/theme_provider.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 3 | 4 | import 'shared_utility_provider.dart'; 5 | 6 | final isDarkProvider = ChangeNotifierProvider((ref) { 7 | return DarkThemeNotifier(ref); 8 | }); 9 | 10 | class DarkThemeNotifier extends ChangeNotifier { 11 | DarkThemeNotifier(this.ref); 12 | Ref ref; 13 | bool getTheme() { 14 | return ref.watch(sharedUtilityProvider).isDarkModeEnabled(); 15 | } 16 | 17 | void toggleTheme() { 18 | ref.watch(sharedUtilityProvider).setDarkModeEnabled( 19 | !ref.watch(sharedUtilityProvider).isDarkModeEnabled(), 20 | ); 21 | notifyListeners(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /lib/models/list_of_todo_model.dart: -------------------------------------------------------------------------------- 1 | import 'todo_model.dart'; 2 | 3 | class ListOfTodoModel { 4 | late List data; 5 | 6 | ListOfTodoModel({ 7 | required this.data, 8 | }); 9 | ListOfTodoModel.fromJson(Map json) { 10 | if (json["data"] != null) { 11 | final v = json["data"]; 12 | final arr0 = []; 13 | v.forEach((v) { 14 | arr0.add(TodoModel.fromJson(v)); 15 | }); 16 | data = arr0; 17 | } 18 | } 19 | Map toJson() { 20 | final Map data = {}; 21 | 22 | final v = this.data; 23 | final arr0 = []; 24 | for (var v in v) { 25 | arr0.add(v.toJson()); 26 | 27 | data["data"] = arr0; 28 | } 29 | return data; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /lib/models/todo_model.dart: -------------------------------------------------------------------------------- 1 | class TodoModel { 2 | late String id; 3 | late String description; 4 | late bool isCompleted; 5 | late bool isPinned; 6 | 7 | TodoModel({ 8 | required this.id, 9 | required this.description, 10 | this.isCompleted = false, 11 | this.isPinned = false, 12 | }); 13 | TodoModel.fromJson(Map json) { 14 | id = json["id"].toString(); 15 | description = json["description"].toString(); 16 | isCompleted = json["isCompleted"] ?? false; 17 | isPinned = json["isPinned"] ?? false; 18 | } 19 | Map toJson() { 20 | final Map data = {}; 21 | data["id"] = id; 22 | data["description"] = description; 23 | data["isCompleted"] = isCompleted; 24 | data["isPinned"] = isPinned; 25 | return data; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /.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_riverpod_todo_app", 9 | "request": "launch", 10 | "type": "dart" 11 | }, 12 | { 13 | "name": "flutter_riverpod_todo_app (profile mode)", 14 | "request": "launch", 15 | "type": "dart", 16 | "flutterMode": "profile" 17 | }, 18 | { 19 | "name": "flutter_riverpod_todo_app (release mode)", 20 | "request": "launch", 21 | "type": "dart", 22 | "flutterMode": "release" 23 | } 24 | ] 25 | } -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /lib/providers/filtered_todos_list_provider.dart: -------------------------------------------------------------------------------- 1 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 2 | 3 | import '../models/list_of_todo_model.dart'; 4 | import 'todo_filter_type_provider.dart'; 5 | import 'todo_list_provider.dart'; 6 | 7 | final filteredTodos = Provider((ref) { 8 | final filter = ref.watch(todoFilterType.state); 9 | final todos = ref.watch(todoListProvider); 10 | 11 | switch (filter.state) { 12 | case TodoFilterType.completed: 13 | return ListOfTodoModel( 14 | data: todos.data.where((todo) => todo.isCompleted).toList(), 15 | ); 16 | 17 | case TodoFilterType.active: 18 | return ListOfTodoModel( 19 | data: todos.data.where((todo) => !todo.isCompleted).toList(), 20 | ); 21 | case TodoFilterType.pinned: 22 | return ListOfTodoModel( 23 | data: todos.data.where((todo) => todo.isPinned).toList(), 24 | ); 25 | case TodoFilterType.all: 26 | return todos; 27 | } 28 | }); 29 | -------------------------------------------------------------------------------- /.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 | 48 | # android key 49 | **/android/key.properties 50 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /android/app/src/main/res/values-night/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /web/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "flutter_riverpod_todo_app", 3 | "short_name": "flutter_riverpod_todo_app", 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 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Sourav Kumar Suman 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 | -------------------------------------------------------------------------------- /test/widget_test.dart: -------------------------------------------------------------------------------- 1 | // This is a basic Flutter widget test. 2 | // 3 | // To perform an interaction with a widget in your test, use the WidgetTester 4 | // utility that Flutter provides. For example, you can send tap and scroll 5 | // gestures. You can also use WidgetTester to find child widgets in the widget 6 | // tree, read text, and verify that the values of widget properties are correct. 7 | 8 | import 'package:flutter/material.dart'; 9 | import 'package:flutter_test/flutter_test.dart'; 10 | 11 | import 'package:flutter_riverpod_todo_app/main.dart'; 12 | 13 | void main() { 14 | testWidgets('Counter increments smoke test', (WidgetTester tester) async { 15 | // Build our app and trigger a frame. 16 | await tester.pumpWidget(const MyApp()); 17 | 18 | // Verify that our counter starts at 0. 19 | expect(find.text('0'), findsOneWidget); 20 | expect(find.text('1'), findsNothing); 21 | 22 | // Tap the '+' icon and trigger a frame. 23 | await tester.tap(find.byIcon(Icons.add)); 24 | await tester.pump(); 25 | 26 | // Verify that our counter has incremented. 27 | expect(find.text('0'), findsNothing); 28 | expect(find.text('1'), findsOneWidget); 29 | }); 30 | } 31 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: flutter_riverpod_todo_app 2 | description: A new Flutter project. 3 | 4 | publish_to: 'none' 5 | version: 1.2.6+4 6 | 7 | environment: 8 | sdk: ">=3.2.6 <4.0.0" 9 | 10 | dependencies: 11 | flutter: 12 | sdk: flutter 13 | cupertino_icons: ^1.0.6 14 | hooks_riverpod: ^2.4.10 15 | flutter_hooks: ^0.20.5 16 | uuid: ^4.3.3 17 | shared_preferences: ^2.2.2 18 | backdrop: ^0.9.1 19 | url_launcher: ^6.2.5 20 | path_provider: ^2.1.2 21 | permission_handler: ^11.3.0 22 | share_plus: ^7.2.2 23 | file_picker: ^6.1.1 24 | carbon_icons: ^0.0.1+2 25 | 26 | dev_dependencies: 27 | flutter_test: 28 | sdk: flutter 29 | flutter_lints: ^1.0.0 30 | 31 | flutter: 32 | uses-material-design: true 33 | assets: 34 | - assets/images/ 35 | fonts: 36 | - family: Rubik 37 | fonts: 38 | - asset: assets/fonts/rubik/Rubik-Regular.ttf 39 | - asset: assets/fonts/rubik/Rubik-Medium.ttf 40 | weight: 500 41 | - asset: assets/fonts/rubik/Rubik-SemiBold.ttf 42 | weight: 600 43 | - asset: assets/fonts/rubik/Rubik-Bold.ttf 44 | weight: 700 45 | - asset: assets/fonts/rubik/Rubik-Light.ttf 46 | weight: 300 -------------------------------------------------------------------------------- /lib/ui/widgets/app_title.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class AppBarTitle extends StatelessWidget { 4 | const AppBarTitle({ 5 | this.fontSize = 24, 6 | this.leadingTitle = '', 7 | this.trailingTitle = '', 8 | Key? key, 9 | }) : super(key: key); 10 | final double? fontSize; 11 | final String leadingTitle; 12 | final String trailingTitle; 13 | @override 14 | Widget build(BuildContext context) { 15 | return Row( 16 | mainAxisAlignment: MainAxisAlignment.center, 17 | children: [ 18 | Text( 19 | leadingTitle, 20 | textAlign: TextAlign.center, 21 | style: Theme.of(context).textTheme.displayMedium?.copyWith( 22 | fontSize: fontSize, 23 | ), 24 | ), 25 | Text( 26 | trailingTitle == '' || leadingTitle == '' ? '' : ' ', 27 | textAlign: TextAlign.center, 28 | style: Theme.of(context).textTheme.displayMedium?.copyWith( 29 | fontSize: fontSize, 30 | ), 31 | ), 32 | Text( 33 | trailingTitle, 34 | textAlign: TextAlign.center, 35 | style: Theme.of(context).textTheme.displayLarge?.copyWith( 36 | fontSize: fontSize, 37 | ), 38 | ), 39 | ], 40 | ); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /lib/ui/widgets/custom_button.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class CustomButton extends StatelessWidget { 4 | const CustomButton( 5 | this.title, { 6 | this.onTap, 7 | Key? key, 8 | }) : super(key: key); 9 | final void Function()? onTap; 10 | final String title; 11 | @override 12 | Widget build(BuildContext context) { 13 | return InkWell( 14 | onTap: onTap, 15 | child: Container( 16 | padding: const EdgeInsets.all(16), 17 | // height: 60, 18 | width: MediaQuery.of(context).size.width * 0.6, 19 | decoration: BoxDecoration( 20 | boxShadow: [ 21 | BoxShadow( 22 | blurRadius: 5, 23 | color: Theme.of(context).colorScheme.secondary.withOpacity(0.54), 24 | offset: const Offset(2, 2), 25 | spreadRadius: 0, 26 | ), 27 | ], 28 | borderRadius: BorderRadius.circular(16), 29 | border: Border.all( 30 | color: Theme.of(context).colorScheme.secondary, 31 | width: 2, 32 | ), 33 | color: Theme.of(context).colorScheme.primary, 34 | ), 35 | alignment: Alignment.center, 36 | child: Text( 37 | title, 38 | textAlign: TextAlign.center, 39 | style: Theme.of(context).textTheme.labelLarge, 40 | ), 41 | ), 42 | ); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 3 | import 'package:shared_preferences/shared_preferences.dart'; 4 | 5 | import 'providers/shared_utility_provider.dart'; 6 | import 'providers/storage_provider.dart'; 7 | import 'providers/theme_provider.dart'; 8 | import 'ui/pages/home_page.dart'; 9 | import 'utils/storage.dart'; 10 | import 'utils/styles/task_app_theme.dart'; 11 | 12 | Future main() async { 13 | WidgetsFlutterBinding.ensureInitialized(); 14 | final SharedPreferences sharedPreferences = 15 | await SharedPreferences.getInstance(); 16 | final storage = LocalStorage(); 17 | runApp( 18 | ProviderScope( 19 | overrides: [ 20 | sharedPreferencesProvider.overrideWithValue(sharedPreferences), 21 | storageProvider.overrideWithValue(storage), 22 | ], 23 | child: const MyApp(), 24 | ), 25 | ); 26 | } 27 | 28 | class MyApp extends StatelessWidget { 29 | const MyApp({Key? key}) : super(key: key); 30 | 31 | @override 32 | Widget build(BuildContext context) { 33 | return Consumer( 34 | builder: (BuildContext context, WidgetRef ref, _) { 35 | final bool isDark = ref.watch(isDarkProvider).getTheme(); 36 | return MaterialApp( 37 | debugShowCheckedModeBanner: false, 38 | theme: isDark ? TaskAppTheme.dark() : TaskAppTheme.light(), 39 | home: const Home(), 40 | ); 41 | }, 42 | ); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | # This file configures the analyzer, which statically analyzes Dart code to 2 | # check for errors, warnings, and lints. 3 | # 4 | # The issues identified by the analyzer are surfaced in the UI of Dart-enabled 5 | # IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be 6 | # invoked from the command line by running `flutter analyze`. 7 | 8 | # The following line activates a set of recommended lints for Flutter apps, 9 | # packages, and plugins designed to encourage good coding practices. 10 | include: package:flutter_lints/flutter.yaml 11 | 12 | linter: 13 | # The lint rules applied to this project can be customized in the 14 | # section below to disable rules from the `package:flutter_lints/flutter.yaml` 15 | # included above or to enable additional rules. A list of all available lints 16 | # and their documentation is published at 17 | # https://dart-lang.github.io/linter/lints/index.html. 18 | # 19 | # Instead of disabling a lint rule for the entire project in the 20 | # section below, it can also be suppressed for a single line of code 21 | # or a specific dart file by using the `// ignore: name_of_lint` and 22 | # `// ignore_for_file: name_of_lint` syntax on the line or in the file 23 | # producing the lint. 24 | rules: 25 | # avoid_print: false # Uncomment to disable the `avoid_print` rule 26 | # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule 27 | 28 | # Additional information about this file can be found at 29 | # https://dart.dev/guides/language/analysis-options 30 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /lib/providers/shared_utility_provider.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 4 | import 'package:shared_preferences/shared_preferences.dart'; 5 | 6 | import '../models/list_of_todo_model.dart'; 7 | import '../utils/constants.dart'; 8 | 9 | final sharedPreferencesProvider = Provider((ref) { 10 | throw UnimplementedError(); 11 | }); 12 | 13 | final sharedUtilityProvider = Provider((ref) { 14 | final _sharedPrefs = ref.watch(sharedPreferencesProvider); 15 | return SharedUtility(sharedPreferences: _sharedPrefs); 16 | }); 17 | 18 | class SharedUtility { 19 | SharedUtility({ 20 | required this.sharedPreferences, 21 | }); 22 | 23 | final SharedPreferences sharedPreferences; 24 | 25 | ListOfTodoModel loadSharedTodoData() { 26 | Map decodeOptions = jsonDecode( 27 | sharedPreferences.getString(sharedPrefTodoListKey) ?? 28 | emptyJsonStringData); 29 | ListOfTodoModel listOfTodoModel = ListOfTodoModel.fromJson(decodeOptions); 30 | return listOfTodoModel; 31 | } 32 | 33 | void saveSharedTodoData(ListOfTodoModel listOfTodoModel) { 34 | if (listOfTodoModel.data.isNotEmpty) { 35 | Map decodeOptions = listOfTodoModel.toJson(); 36 | String sharedData = jsonEncode( 37 | ListOfTodoModel.fromJson(decodeOptions), 38 | ); 39 | sharedPreferences.setString(sharedPrefTodoListKey, sharedData); 40 | } else { 41 | sharedPreferences.setString(sharedPrefTodoListKey, emptyJsonStringData); 42 | } 43 | } 44 | 45 | bool isDarkModeEnabled() { 46 | return sharedPreferences.getBool(sharedDarkModeKey) ?? false; 47 | } 48 | 49 | void setDarkModeEnabled(bool value) { 50 | sharedPreferences.setBool(sharedDarkModeKey, value); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | NSPhotoLibraryUsageDescription 6 | This app requires to save a backup file on device 7 | CFBundleDevelopmentRegion 8 | $(DEVELOPMENT_LANGUAGE) 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | flutter_riverpod_todo_app 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 | 47 | 48 | -------------------------------------------------------------------------------- /lib/ui/widgets/toolbar.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 3 | 4 | import '../../providers/todo_filter_type_provider.dart'; 5 | import '../../utils/unique_keys.dart'; 6 | import 'category_button.dart'; 7 | 8 | class ToolBar extends StatelessWidget { 9 | const ToolBar({required this.filter, Key? key}) : super(key: key); 10 | final StateController filter; 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | return Material( 15 | borderRadius: BorderRadius.circular(8), 16 | color: Theme.of(context).colorScheme.secondary, 17 | child: Padding( 18 | padding: const EdgeInsets.symmetric(vertical: 4.0, horizontal: 4.0), 19 | child: Column( 20 | children: [ 21 | Wrap( 22 | children: [ 23 | CategoryButton( 24 | title: 'All', 25 | buttonFilter: TodoFilterType.all, 26 | tooltipKey: allFilterKey, 27 | filter: filter, 28 | ), 29 | CategoryButton( 30 | title: 'Active', 31 | buttonFilter: TodoFilterType.active, 32 | tooltipKey: activeFilterKey, 33 | filter: filter, 34 | ), 35 | CategoryButton( 36 | title: 'Pinned', 37 | buttonFilter: TodoFilterType.pinned, 38 | tooltipKey: pinnedFilterKey, 39 | filter: filter, 40 | ), 41 | CategoryButton( 42 | title: 'Done', 43 | buttonFilter: TodoFilterType.completed, 44 | tooltipKey: completedFilterKey, 45 | filter: filter, 46 | ), 47 | ], 48 | ), 49 | ], 50 | ), 51 | ), 52 | ); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /lib/ui/widgets/category_button.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 3 | 4 | import '../../providers/todo_filter_type_provider.dart'; 5 | 6 | class CategoryButton extends StatelessWidget { 7 | const CategoryButton({ 8 | required this.title, 9 | required this.buttonFilter, 10 | required this.tooltipKey, 11 | required this.filter, 12 | Key? key, 13 | }) : super(key: key); 14 | final String title; 15 | final TodoFilterType buttonFilter; 16 | final UniqueKey tooltipKey; 17 | final StateController filter; 18 | @override 19 | Widget build(BuildContext context) { 20 | final Color activeColor = Theme.of(context).colorScheme.primary; 21 | final Color nonActiveColor = Theme.of(context).colorScheme.secondary; 22 | Color? textColorFor(TodoFilterType value) { 23 | return filter.state == value ? nonActiveColor : activeColor; 24 | } 25 | 26 | Color? backgroundColorFor(TodoFilterType value) { 27 | return filter.state == value ? activeColor : Colors.transparent; 28 | } 29 | 30 | return Tooltip( 31 | key: tooltipKey, 32 | message: title, 33 | child: TextButton( 34 | onPressed: () => filter.state = buttonFilter, 35 | style: ButtonStyle( 36 | textStyle: MaterialStateProperty.all( 37 | Theme.of(context).textTheme.labelLarge, 38 | ), 39 | visualDensity: VisualDensity.compact, 40 | foregroundColor: MaterialStateProperty.all( 41 | textColorFor(buttonFilter), 42 | ), 43 | overlayColor: MaterialStateProperty.all( 44 | Colors.transparent, 45 | ), 46 | backgroundColor: MaterialStateProperty.all( 47 | backgroundColorFor(buttonFilter), 48 | ), 49 | ), 50 | child: Text( 51 | title, 52 | textAlign: TextAlign.center, 53 | ), 54 | ), 55 | ); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /lib/utils/hooks/scroll_controller_hook.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/rendering.dart'; 2 | import 'package:flutter/widgets.dart'; 3 | import 'package:flutter_hooks/flutter_hooks.dart'; 4 | 5 | ScrollController useControllerForAnimation( 6 | AnimationController backwardAnimController, 7 | AnimationController forwardAnimController) => 8 | use(_ScrollControllerForAnimationHook( 9 | backwardAnimController: backwardAnimController, 10 | forwardAnimController: forwardAnimController)); 11 | 12 | class _ScrollControllerForAnimationHook extends Hook { 13 | final AnimationController backwardAnimController; 14 | final AnimationController forwardAnimController; 15 | const _ScrollControllerForAnimationHook({ 16 | required this.backwardAnimController, 17 | required this.forwardAnimController, 18 | }); 19 | @override 20 | _ScrollControllerForAnimationHookState createState() => 21 | _ScrollControllerForAnimationHookState(); 22 | } 23 | 24 | class _ScrollControllerForAnimationHookState 25 | extends HookState { 26 | late ScrollController _scrollController; 27 | @override 28 | void initHook() { 29 | _scrollController = ScrollController(); 30 | 31 | _scrollController.addListener( 32 | () { 33 | if (_scrollController.position.atEdge && 34 | _scrollController.position.pixels == 0) { 35 | hook.backwardAnimController.forward(); 36 | hook.forwardAnimController.reverse(); 37 | } else if (_scrollController.position.userScrollDirection == 38 | ScrollDirection.reverse) { 39 | hook.backwardAnimController.reverse(); 40 | hook.forwardAnimController.forward(); 41 | } 42 | }, 43 | ); 44 | } 45 | 46 | @override 47 | ScrollController build(BuildContext context) => _scrollController; 48 | 49 | @override 50 | void dispose() { 51 | _scrollController.dispose(); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 23 | 24 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /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 | // release keys 10 | def keystoreProperties = new Properties() 11 | def keystorePropertiesFile = rootProject.file('key.properties') 12 | if (keystorePropertiesFile.exists()) { 13 | keystoreProperties.load(new FileInputStream(keystorePropertiesFile)) 14 | } 15 | 16 | 17 | def flutterRoot = localProperties.getProperty('flutter.sdk') 18 | if (flutterRoot == null) { 19 | throw new Exception("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") 20 | } 21 | 22 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 23 | if (flutterVersionCode == null) { 24 | flutterVersionCode = '1' 25 | } 26 | 27 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 28 | if (flutterVersionName == null) { 29 | flutterVersionName = '1.0' 30 | } 31 | 32 | apply plugin: 'com.android.application' 33 | apply plugin: 'kotlin-android' 34 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 35 | 36 | android { 37 | compileSdkVersion 34 38 | 39 | compileOptions { 40 | sourceCompatibility JavaVersion.VERSION_1_8 41 | targetCompatibility JavaVersion.VERSION_1_8 42 | } 43 | 44 | kotlinOptions { 45 | jvmTarget = '1.8' 46 | } 47 | 48 | sourceSets { 49 | main.java.srcDirs += 'src/main/kotlin' 50 | } 51 | 52 | defaultConfig { 53 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 54 | applicationId "com.example.flutter_riverpod_todo_app" 55 | minSdkVersion 21 56 | targetSdkVersion 34 57 | versionCode flutterVersionCode.toInteger() 58 | versionName flutterVersionName 59 | } 60 | signingConfigs { 61 | release { 62 | keyAlias keystoreProperties['keyAlias'] 63 | keyPassword keystoreProperties['keyPassword'] 64 | storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null 65 | storePassword keystoreProperties['storePassword'] 66 | } 67 | } 68 | 69 | buildTypes { 70 | release { 71 | signingConfig signingConfigs.release 72 | minifyEnabled true 73 | shrinkResources true 74 | } 75 | } 76 | } 77 | 78 | flutter { 79 | source '../..' 80 | } 81 | 82 | dependencies { 83 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 84 | } 85 | -------------------------------------------------------------------------------- /lib/ui/pages/back_layer_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:carbon_icons/carbon_icons.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 4 | 5 | import '../../providers/export_providers.dart'; 6 | import 'about_us_page.dart'; 7 | import 'backup_and_restore.dart'; 8 | import 'instructions_page.dart'; 9 | 10 | class BackLayerPage extends StatelessWidget { 11 | const BackLayerPage({ 12 | Key? key, 13 | }) : super(key: key); 14 | 15 | @override 16 | Widget build(BuildContext context) { 17 | return Container( 18 | // height: MediaQuery.of(context).size.height * 0.7, 19 | decoration: BoxDecoration( 20 | borderRadius: BorderRadius.circular(16), 21 | border: Border.all( 22 | color: Theme.of(context).colorScheme.secondary, width: 4) 23 | // color: Theme.of(context).primaryColor, 24 | ), 25 | margin: const EdgeInsets.fromLTRB(8, 0, 8, 8), 26 | child: ListView( 27 | shrinkWrap: true, 28 | children: [ 29 | ListTile( 30 | title: const Text('Backup & Restore'), 31 | leading: const Icon(CarbonIcons.renew), 32 | onTap: () { 33 | Navigator.push( 34 | context, 35 | MaterialPageRoute( 36 | builder: (context) => const BackupAndRestorePage(), 37 | ), 38 | ); 39 | }, 40 | ), 41 | ListTile( 42 | title: const Text('Instructions'), 43 | leading: const Icon(CarbonIcons.information), 44 | onTap: () { 45 | Navigator.push( 46 | context, 47 | MaterialPageRoute( 48 | builder: (context) => const InstructionPage(), 49 | ), 50 | ); 51 | }, 52 | ), 53 | Consumer( 54 | builder: (context, ref, _) { 55 | return SwitchListTile( 56 | title: const Text('Dark Mode'), 57 | activeColor: Colors.white, 58 | secondary: const Icon(CarbonIcons.asleep), 59 | value: ref.watch(isDarkProvider).getTheme(), 60 | onChanged: (bool value) { 61 | ref.read(isDarkProvider.notifier).toggleTheme(); 62 | }, 63 | ); 64 | }, 65 | ), 66 | ListTile( 67 | focusColor: Colors.red, 68 | leading: const Icon(CarbonIcons.enterprise), 69 | title: const Text('About US'), 70 | onTap: () { 71 | Navigator.push( 72 | context, 73 | MaterialPageRoute( 74 | builder: (context) => const AboutUsPage(), 75 | ), 76 | ); 77 | }, 78 | ), 79 | ], 80 | ), 81 | ); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /lib/ui/pages/about_us_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:carbon_icons/carbon_icons.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:url_launcher/url_launcher.dart'; 4 | 5 | import '../../utils/constants.dart'; 6 | import '../widgets/app_title.dart'; 7 | 8 | class AboutUsPage extends StatelessWidget { 9 | const AboutUsPage({Key? key}) : super(key: key); 10 | 11 | @override 12 | Widget build(BuildContext context) { 13 | void _launchUrl(Uri _url) async { 14 | await canLaunchUrl(_url) 15 | ? await launchUrl(_url) 16 | : throw 'Could not launch $_url'; 17 | } 18 | 19 | return Scaffold( 20 | appBar: AppBar( 21 | actions: const [ 22 | Icon( 23 | Icons.ac_unit, 24 | color: Colors.transparent, 25 | ), 26 | Icon( 27 | Icons.ac_unit, 28 | color: Colors.transparent, 29 | ) 30 | ], 31 | title: const AppBarTitle( 32 | leadingTitle: 'About', 33 | trailingTitle: 'Us', 34 | ), 35 | centerTitle: true, 36 | ), 37 | body: Padding( 38 | padding: const EdgeInsets.all(16), 39 | child: Column( 40 | mainAxisAlignment: MainAxisAlignment.center, 41 | crossAxisAlignment: CrossAxisAlignment.stretch, 42 | children: [ 43 | const Spacer(), 44 | const Icon( 45 | CarbonIcons.task, 46 | size: 64, 47 | ), 48 | const SizedBox( 49 | height: 8, 50 | ), 51 | const AppBarTitle( 52 | trailingTitle: 'Todopad', 53 | fontSize: 36, 54 | ), 55 | const AppBarTitle( 56 | leadingTitle: 'by', 57 | fontSize: 16, 58 | ), 59 | const AppBarTitle( 60 | leadingTitle: 'App', 61 | trailingTitle: 'Dexon', 62 | fontSize: 20, 63 | ), 64 | const SizedBox( 65 | height: 48, 66 | ), 67 | const Spacer(), 68 | ButtonBar( 69 | alignment: MainAxisAlignment.center, 70 | children: [ 71 | IconButton( 72 | onPressed: () => _launchUrl(Uri.parse(fbUrl)), 73 | icon: const Icon( 74 | CarbonIcons.logo_facebook, 75 | size: 32, 76 | ), 77 | ), 78 | IconButton( 79 | onPressed: () => _launchUrl(Uri.parse(mailUrl)), 80 | icon: const Icon( 81 | CarbonIcons.email, 82 | size: 32, 83 | ), 84 | ), 85 | IconButton( 86 | onPressed: () => _launchUrl(Uri.parse(githubUrl)), 87 | icon: const Icon( 88 | CarbonIcons.logo_github, 89 | size: 32, 90 | ), 91 | ), 92 | ], 93 | ), 94 | ], 95 | ), 96 | ), 97 | ); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 💙An Elegant "Todopad" Made With Flutter💙 2 | [![made-with-flutter](https://img.shields.io/badge/Made%20with-Flutter-1f425f.svg)](https://flutter.dev/) 3 | ![release](https://img.shields.io/github/v/release/thealphaapp/flutter_riverpod_todo_app) 4 | ![License](https://img.shields.io/github/license/TheAlphaApp/flutter_riverpod_todo_app) 5 | ![Downloads](https://img.shields.io/github/downloads/TheAlphaApp/flutter_riverpod_todo_app/total?label=download%40github) 6 | ## 🌟Star🌟 the repo if you like it 7 | This is a FOSS todo app with local backup and restore functionality made with flutter. 8 | 9 | > Special Mention : It uses [riverpod](https://riverpod.dev/) as state management solution. 10 | 11 | ## ⬇️Download 12 | ### v1.2.6+4 (Latest) 13 | 14 | [Download from GitHub](https://github.com/TheAlphaApp/flutter_riverpod_todo_app/releases/) 15 | 16 | [Download from IzzyOnDroid](https://apt.izzysoft.de/fdroid/index/apk/com.example.flutter_riverpod_todo_app) 17 | 18 | ### v1.0.0 19 | > There will not be any future update on play store. It is recommended to download from the [github release](https://github.com/TheAlphaApp/flutter_riverpod_todo_app/releases/). P.S. If you want to show your love, you can leave a rating on the play store. 20 | 21 | [Download from IzzyOnDroid](https://play.google.com/store/apps/details?id=com.appdexon.todopad) 22 | ## 📱Screenshots 23 | 24 | ![](https://github.com/TheAlphaApp/flutter_riverpod_todo_app/blob/main/screenshots/Frame%20-%201.png?raw=true)|![](https://github.com/TheAlphaApp/flutter_riverpod_todo_app/blob/main/screenshots/Frame%20-%202.png?raw=true)|![](https://github.com/TheAlphaApp/flutter_riverpod_todo_app/blob/main/screenshots/Frame%20-%203.png?raw=true) 25 | :-------------------------:|:-------------------------:|:---------------------: 26 | ![](https://github.com/TheAlphaApp/flutter_riverpod_todo_app/blob/main/screenshots/Frame%20-%204.png?raw=true)|![](https://github.com/TheAlphaApp/flutter_riverpod_todo_app/blob/main/screenshots/Frame%20-%205.png?raw=true)|![](https://github.com/TheAlphaApp/flutter_riverpod_todo_app/blob/main/screenshots/Frame%20-%206.png?raw=true) 27 | ### 🎯It has many features: 28 | - Write a todo 29 | - Edit a todo 30 | - Mark todo as favourite 31 | - Mark todo as completed 32 | - Different sections for each 33 | - Create a local backup with time and date mentioned 34 | - Restore any backup from backup list on the app 35 | - Very easy and accessible features 36 | - Restore backup from a valid json file 37 | 38 | ### ⚠️Note : 39 | This app tested only on android. On other platform than android, this app may or may not work as expected. 40 | 41 | 42 | ## 🔰Getting Started 43 | A few resources to get you started if this is your first Flutter project: 44 | 45 | - [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab) 46 | - [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook) 47 | 48 | For help getting started with Flutter, view our 49 | [online documentation](https://flutter.dev/docs), which offers tutorials, 50 | samples, guidance on mobile development, and a full API reference. 51 | -------------------------------------------------------------------------------- /lib/utils/storage.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | import 'dart:io'; 3 | 4 | import 'package:file_picker/file_picker.dart'; 5 | import 'package:path_provider/path_provider.dart'; 6 | import 'package:permission_handler/permission_handler.dart'; 7 | import 'package:share_plus/share_plus.dart'; 8 | 9 | import '../models/list_of_todo_model.dart'; 10 | import 'constants.dart'; 11 | 12 | abstract class Storage { 13 | Future writeData(ListOfTodoModel listOfTodoModel); 14 | Future readData(File selectedFile); 15 | Future readFromFilePicker(); 16 | Future> getListOfBackups(); 17 | void share(String path); 18 | } 19 | 20 | class LocalStorage extends Storage { 21 | Future get _localPath async { 22 | final directory = await getExternalStorageDirectory(); 23 | var path = directory?.path; 24 | return path!; 25 | } 26 | 27 | Future get _localFile async { 28 | DateTime now = DateTime.now(); 29 | final path = await _localPath; 30 | return File('$path/backup-$now.json'); 31 | } 32 | 33 | @override 34 | Future> getListOfBackups() async { 35 | List listOfFiles = []; 36 | final directory = await getExternalStorageDirectory(); 37 | List allFiles = directory?.listSync() ?? []; 38 | for (var files in allFiles) { 39 | listOfFiles.add(files.path); 40 | } 41 | return listOfFiles; 42 | } 43 | 44 | @override 45 | Future writeData(ListOfTodoModel listOfTodoModel) async { 46 | // 3 47 | if (!await Permission.storage.request().isGranted) { 48 | return Future.value(null); 49 | } 50 | 51 | final file = await _localFile; 52 | if (!await file.exists()) { 53 | await file.create(recursive: true); 54 | } 55 | if (listOfTodoModel.data.isNotEmpty) { 56 | Map decodeOptions = listOfTodoModel.toJson(); 57 | String encodedData = jsonEncode( 58 | ListOfTodoModel.fromJson(decodeOptions), 59 | ); 60 | return file.writeAsString(encodedData); 61 | } else { 62 | return file.writeAsString(emptyJsonStringData); 63 | } 64 | } 65 | 66 | @override 67 | Future readData( 68 | File selectedFile, 69 | ) async { 70 | // 1 71 | try { 72 | final jsonContents = await selectedFile.readAsString(); // 3 73 | Map jsonResponse = jsonDecode(jsonContents); // 4 74 | ListOfTodoModel listOfTodoModel = ListOfTodoModel.fromJson(jsonResponse); 75 | return listOfTodoModel; // 5 76 | } catch (e) { 77 | // If encountering an empty array 78 | return ListOfTodoModel(data: []); 79 | } 80 | } 81 | 82 | @override 83 | Future readFromFilePicker() async { 84 | FilePickerResult? result = await FilePicker.platform.pickFiles(); 85 | // 1 86 | if (result == null || !result.files.single.path!.endsWith('.json')) { 87 | return null; 88 | } 89 | 90 | File file = File(result.files.single.path!); // 3 91 | var listOfTodoModel = readData(file); // 4 92 | writeData(await listOfTodoModel); 93 | return listOfTodoModel; 94 | } 95 | 96 | @override 97 | void share(String path) async { 98 | // 1 99 | Share.shareXFiles([XFile(path)], text: 'Back up'); // 2 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /lib/ui/pages/backup_and_restore.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 5 | 6 | import '../../models/list_of_todo_model.dart'; 7 | import '../../providers/export_providers.dart'; 8 | import '../widgets/app_title.dart'; 9 | import '../widgets/custom_button.dart'; 10 | import 'backup_list.dart'; 11 | 12 | class BackupAndRestorePage extends ConsumerWidget { 13 | const BackupAndRestorePage({Key? key}) : super(key: key); 14 | 15 | @override 16 | Widget build(BuildContext context, WidgetRef ref) { 17 | final storage = ref.watch(storageProvider); 18 | void _showAlert(String msg) { 19 | showDialog( 20 | context: context, 21 | builder: (context) => AlertDialog( 22 | content: Text(msg), 23 | actions: [ 24 | TextButton( 25 | onPressed: () => Navigator.pop(context), 26 | child: Text( 27 | 'OK', 28 | style: TextStyle( 29 | color: Theme.of(context).colorScheme.secondary, 30 | ), 31 | ), 32 | ) 33 | ], 34 | ), 35 | ); 36 | } 37 | 38 | Future _writeListOfTodoModel() async { 39 | ListOfTodoModel listOfTodoModel = ref.watch(todoListProvider); 40 | 41 | File? file = await storage.writeData(listOfTodoModel); 42 | if (file != null) { 43 | _showAlert('Backup file created.'); 44 | return file; 45 | } else { 46 | _showAlert( 47 | 'Some error happened while taking backup.\nCheck "app info" if storage permission is granted or not.'); 48 | return null; 49 | } 50 | } 51 | 52 | void _readListOfTodoModelFromFilePicker() async { 53 | ListOfTodoModel? listOfTodoModel = await storage.readFromFilePicker(); 54 | if (listOfTodoModel != null) { 55 | ref.read(todoListProvider.notifier) 56 | ..overrideData(listOfTodoModel) 57 | ..saveData(); 58 | _showAlert('Data Loaded from the file'); 59 | } 60 | if (listOfTodoModel == null) { 61 | _showAlert('Data can\'t be loaded.\nChoose a valid file.'); 62 | } 63 | } 64 | 65 | return Scaffold( 66 | appBar: AppBar( 67 | title: const AppBarTitle( 68 | leadingTitle: 'Backup &', 69 | trailingTitle: 'Restore', 70 | ), 71 | actions: const [ 72 | Icon( 73 | Icons.ac_unit, 74 | color: Colors.transparent, 75 | ), 76 | Icon( 77 | Icons.ac_unit, 78 | color: Colors.transparent, 79 | ) 80 | ], 81 | ), 82 | body: SizedBox( 83 | width: double.infinity, 84 | child: Column( 85 | mainAxisAlignment: MainAxisAlignment.spaceEvenly, 86 | crossAxisAlignment: CrossAxisAlignment.center, 87 | children: [ 88 | CustomButton( 89 | 'Create backup', 90 | onTap: _writeListOfTodoModel, 91 | ), 92 | CustomButton( 93 | 'List of backup files', 94 | onTap: () async { 95 | List listOfFiles = await storage.getListOfBackups(); 96 | listOfFiles = listOfFiles.reversed.toList(); 97 | Navigator.push( 98 | context, 99 | MaterialPageRoute( 100 | builder: (context) => BackupList( 101 | listOfFiles: listOfFiles, 102 | ), 103 | ), 104 | ); 105 | }, 106 | ), 107 | CustomButton( 108 | 'Restore backup from storage', 109 | onTap: _readListOfTodoModelFromFilePicker, 110 | ), 111 | ], 112 | ), 113 | ), 114 | ); 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /lib/providers/todo_list_provider.dart: -------------------------------------------------------------------------------- 1 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 2 | import 'package:uuid/uuid.dart'; 3 | 4 | import '../models/list_of_todo_model.dart'; 5 | import '../models/todo_model.dart'; 6 | import 'shared_utility_provider.dart'; 7 | 8 | const _uuid = Uuid(); 9 | final todoListProvider = StateNotifierProvider( 10 | (ref) { 11 | final shareutil = ref.watch(sharedUtilityProvider); 12 | return TodoList(ListOfTodoModel(data: []), shareutil); 13 | }, 14 | ); 15 | 16 | class TodoList extends StateNotifier { 17 | TodoList(ListOfTodoModel initialTodos, this.sharedUtility) 18 | : super( 19 | initialTodos, 20 | ); 21 | final SharedUtility sharedUtility; 22 | 23 | void overrideData(ListOfTodoModel listOfTodoModel) { 24 | if (listOfTodoModel.data.isNotEmpty) { 25 | state = listOfTodoModel; 26 | } 27 | } 28 | 29 | void saveData() { 30 | sharedUtility.saveSharedTodoData(state); 31 | } 32 | 33 | void loadData() { 34 | final data = sharedUtility.loadSharedTodoData(); 35 | state = data; 36 | } 37 | 38 | /// Adds a new [TodoModel] to the list. 39 | void add(String description) { 40 | state = ListOfTodoModel(data: [ 41 | ...state.data, 42 | TodoModel(id: _uuid.v4(), description: description), 43 | ]); 44 | saveData(); 45 | } 46 | 47 | // Toggle favourite 48 | void togglePinned(String id) async { 49 | state = ListOfTodoModel(data: [ 50 | for (final todo in state.data) 51 | if (todo.id == id) 52 | TodoModel( 53 | id: todo.id, 54 | description: todo.description, 55 | isCompleted: todo.isCompleted, 56 | isPinned: !todo.isPinned, 57 | ) 58 | else 59 | todo, 60 | ]); 61 | saveData(); 62 | } 63 | 64 | /// Toggle the state of [TodoModel]. 65 | void toggle(String id) { 66 | state = ListOfTodoModel(data: [ 67 | for (final todo in state.data) 68 | if (todo.id == id) 69 | TodoModel( 70 | id: todo.id, 71 | description: todo.description, 72 | isCompleted: !todo.isCompleted, 73 | isPinned: todo.isPinned, 74 | ) 75 | else 76 | todo, 77 | ]); 78 | saveData(); 79 | } 80 | 81 | /// Edit the description of [TodoModel]. 82 | void edit({required String id, required String description}) { 83 | state = ListOfTodoModel(data: [ 84 | for (final todo in state.data) 85 | if (todo.id == id) 86 | TodoModel( 87 | id: todo.id, 88 | description: description, 89 | isCompleted: todo.isCompleted, 90 | isPinned: todo.isPinned, 91 | ) 92 | else 93 | todo, 94 | ]); 95 | saveData(); 96 | } 97 | 98 | /// Removes a [TodoModel] from the list. 99 | 100 | void remove(TodoModel target) { 101 | state = ListOfTodoModel( 102 | data: state.data.where((todo) => todo.id != target.id).toList()); 103 | 104 | saveData(); 105 | } 106 | 107 | /// Reorders the list by moving an item from [oldIndex] to [newIndex]. 108 | /// 109 | /// This method takes the [oldIndex] and [newIndex] as parameters and 110 | /// reorders the list accordingly. It retrieves the list of items from 111 | /// the current state, removes the item at the [oldIndex], and inserts 112 | /// it at the [newIndex]. Finally, it updates the state with the new 113 | /// list and saves the data. 114 | void reorder(int oldIndex, int newIndex) { 115 | /// If the new index is greater than the old index, we need to subtract 1 from the new index. 116 | /// This is because when we remove the item from the old index, the items below it will shift up by one. 117 | if (newIndex > oldIndex) { 118 | newIndex -= 1; 119 | } 120 | final items = state.data; 121 | final item = items.removeAt(oldIndex); 122 | items.insert(newIndex, item); 123 | state = ListOfTodoModel(data: items); 124 | saveData(); 125 | } 126 | } -------------------------------------------------------------------------------- /web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | flutter_riverpod_todo_app 30 | 31 | 32 | 33 | 36 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /lib/ui/pages/backup_list.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:carbon_icons/carbon_icons.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 6 | 7 | import '../../models/list_of_todo_model.dart'; 8 | import '../../providers/export_providers.dart'; 9 | import '../widgets/app_title.dart'; 10 | 11 | class BackupList extends ConsumerWidget { 12 | const BackupList({required this.listOfFiles, Key? key}) : super(key: key); 13 | final List listOfFiles; 14 | 15 | @override 16 | Widget build(BuildContext context, WidgetRef ref) { 17 | final storage = ref.watch(storageProvider); 18 | 19 | void _restoreBackup(ListOfTodoModel listOfTodoModel) { 20 | ref.read(todoListProvider.notifier) 21 | ..overrideData(listOfTodoModel) 22 | ..saveData(); 23 | Navigator.pop(context); 24 | ScaffoldMessenger.of(context) 25 | ..hideCurrentSnackBar() 26 | ..showSnackBar( 27 | const SnackBar( 28 | content: Text("Data Restored"), 29 | backgroundColor: Colors.green, 30 | duration: Duration(seconds: 2), 31 | ), 32 | ); 33 | } 34 | 35 | void _restoreDataAlert(ListOfTodoModel listOfTodoModel) { 36 | showDialog( 37 | context: context, 38 | builder: (context) => AlertDialog( 39 | content: const Text("Restore this backup?"), 40 | actions: [ 41 | TextButton( 42 | onPressed: () => Navigator.pop(context), 43 | child: Text( 44 | 'Cancel', 45 | style: TextStyle( 46 | color: Theme.of(context).colorScheme.secondary, 47 | ), 48 | ), 49 | ), 50 | TextButton( 51 | onPressed: () => _restoreBackup(listOfTodoModel), 52 | child: Text( 53 | 'Restore', 54 | style: TextStyle( 55 | color: Theme.of(context).colorScheme.secondary, 56 | ), 57 | ), 58 | ), 59 | ], 60 | ), 61 | ); 62 | } 63 | 64 | void _showAlert(int index) async { 65 | ListOfTodoModel? listOfTodoModel = await storage.readData( 66 | File(listOfFiles[index]), 67 | ); 68 | if (listOfTodoModel != null) { 69 | _restoreDataAlert(listOfTodoModel); 70 | } else { 71 | ScaffoldMessenger.of(context) 72 | ..hideCurrentSnackBar 73 | ..showSnackBar( 74 | const SnackBar( 75 | content: Text("Data is Empty!"), 76 | backgroundColor: Colors.red, 77 | duration: Duration(seconds: 2), 78 | ), 79 | ); 80 | } 81 | } 82 | 83 | void _shareBackup(int index) { 84 | storage.share(listOfFiles[index]); 85 | } 86 | 87 | return Scaffold( 88 | appBar: AppBar( 89 | title: const AppBarTitle( 90 | leadingTitle: 'Backup', 91 | trailingTitle: 'List', 92 | ), 93 | centerTitle: true, 94 | actions: const [ 95 | Icon( 96 | Icons.ac_unit, 97 | color: Colors.transparent, 98 | ), 99 | Icon( 100 | Icons.ac_unit, 101 | color: Colors.transparent, 102 | ), 103 | ], 104 | ), 105 | body: SizedBox( 106 | width: MediaQuery.of(context).size.width, 107 | child: Padding( 108 | padding: const EdgeInsets.all(16), 109 | child: ListView.builder( 110 | itemCount: listOfFiles.length, 111 | itemBuilder: (context, index) => Card( 112 | child: ListTile( 113 | onTap: () => _showAlert(index), 114 | title: Text( 115 | listOfFiles[index] 116 | 117 | /// to show only the file name, 118 | .substring(listOfFiles[index].length - 38, 119 | listOfFiles[index].length), 120 | ), 121 | leading: const Icon(CarbonIcons.restart), 122 | trailing: TextButton( 123 | onPressed: () => _shareBackup(index), 124 | child: const Icon(CarbonIcons.share), 125 | ), 126 | ), 127 | ), 128 | ), 129 | ), 130 | ), 131 | ); 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /lib/ui/widgets/todo_item.dart: -------------------------------------------------------------------------------- 1 | import 'package:carbon_icons/carbon_icons.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_hooks/flutter_hooks.dart'; 4 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 5 | 6 | import '../../providers/current_todo_provider.dart'; 7 | import '../../providers/todo_list_provider.dart'; 8 | 9 | class TodoItem extends HookConsumerWidget { 10 | const TodoItem({Key? key}) : super(key: key); 11 | 12 | @override 13 | Widget build(BuildContext context, WidgetRef ref) { 14 | final todo = ref.watch(currentTodo); 15 | //hooks 16 | final itemFocusNode = useFocusNode(); 17 | // listen to focus chances 18 | useListenable(itemFocusNode); 19 | final isFocused = itemFocusNode.hasFocus; 20 | final textEditingController = useTextEditingController(); 21 | final textFieldFocusNode = useFocusNode(); 22 | 23 | return Container( 24 | decoration: BoxDecoration( 25 | color: Theme.of(context).colorScheme.primary, 26 | ), 27 | child: Focus( 28 | focusNode: itemFocusNode, 29 | onFocusChange: (focused) { 30 | if (focused) { 31 | textEditingController.text = todo.description; 32 | } else { 33 | // Commit changes only when the textfield is unfocused, for performance 34 | ref 35 | .read(todoListProvider.notifier) 36 | .edit(id: todo.id, description: textEditingController.text); 37 | } 38 | }, 39 | child: Column( 40 | children: [ 41 | Padding( 42 | padding: const EdgeInsets.symmetric(vertical: 4.0), 43 | child: ListTile( 44 | onTap: () { 45 | itemFocusNode.requestFocus(); 46 | textFieldFocusNode.requestFocus(); 47 | }, 48 | leading: GestureDetector( 49 | onTap: () => 50 | ref.read(todoListProvider.notifier).toggle(todo.id), 51 | child: Padding( 52 | padding: const EdgeInsets.all(4.0), 53 | child: todo.isCompleted 54 | ? Icon(CarbonIcons.checkmark_outline, 55 | size: 32.0, 56 | color: Theme.of(context).colorScheme.secondary) 57 | : Icon( 58 | CarbonIcons.radio_button, 59 | size: 32.0, 60 | color: Theme.of(context) 61 | .colorScheme 62 | .secondary 63 | .withOpacity(0.45), 64 | ), 65 | ), 66 | ), 67 | title: isFocused 68 | ? Padding( 69 | padding: const EdgeInsets.symmetric(vertical: 8.0), 70 | child: TextField( 71 | autofocus: true, 72 | focusNode: textFieldFocusNode, 73 | controller: textEditingController, 74 | 75 | ///due to bug in indic keyboard . It fills suggestion automatically two times. 76 | enableSuggestions: false, 77 | cursorColor: Theme.of(context).colorScheme.secondary, 78 | decoration: 79 | const InputDecoration(labelText: 'Edit Task')), 80 | ) 81 | : Text( 82 | todo.description, 83 | style: TextStyle( 84 | decoration: todo.isCompleted 85 | ? TextDecoration.lineThrough 86 | : TextDecoration.none, 87 | ), 88 | ), 89 | trailing: GestureDetector( 90 | onTap: () => 91 | ref.read(todoListProvider.notifier).togglePinned(todo.id), 92 | child: todo.isPinned 93 | ? Icon( 94 | CarbonIcons.pin_filled, 95 | size: 24.0, 96 | color: Theme.of(context).colorScheme.secondary, 97 | ) 98 | : Icon( 99 | CarbonIcons.pin, 100 | size: 24.0, 101 | color: Theme.of(context) 102 | .colorScheme 103 | .secondary 104 | .withOpacity(0.45), // 105 | ), 106 | ), 107 | ), 108 | ), 109 | const SizedBox( 110 | height: 2, 111 | ), 112 | const Divider( 113 | height: 1, 114 | thickness: 1, 115 | ), 116 | ], 117 | ), 118 | ), 119 | ); 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /lib/ui/pages/instructions_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import '../../utils/constants.dart'; 4 | import '../widgets/app_title.dart'; 5 | 6 | class InstructionPage extends StatelessWidget { 7 | const InstructionPage({Key? key}) : super(key: key); 8 | 9 | @override 10 | Widget build(BuildContext context) { 11 | TextStyle? headline6 = Theme.of(context).textTheme.titleLarge; 12 | TextStyle? bodyText1 = Theme.of(context).textTheme.bodyLarge; 13 | return Scaffold( 14 | appBar: AppBar( 15 | title: const AppBarTitle( 16 | leadingTitle: 'FAQs', 17 | ), 18 | actions: const [ 19 | Icon( 20 | Icons.ac_unit, 21 | color: Colors.transparent, 22 | ), 23 | Icon( 24 | Icons.ac_unit, 25 | color: Colors.transparent, 26 | ) 27 | ], 28 | ), 29 | body: SingleChildScrollView( 30 | child: Padding( 31 | padding: const EdgeInsets.all(16.0), 32 | child: Column( 33 | crossAxisAlignment: CrossAxisAlignment.start, 34 | children: [ 35 | Text( 36 | 'How to add a todo?', 37 | style: headline6, 38 | ), 39 | Text( 40 | 'Tap on the big box "What\'s needs to be done?" given above on the homepage. And just type your todo and then press enter.', 41 | style: bodyText1, 42 | ), 43 | const Divider(height: 16), 44 | Text( 45 | 'How to mark a todo as completed?', 46 | style: headline6, 47 | ), 48 | Text( 49 | 'Tap on the circle icon button leading the todo name.', 50 | style: bodyText1, 51 | ), 52 | const Divider(height: 16), 53 | Text( 54 | 'How to permanently delete a todo?', 55 | style: headline6, 56 | ), 57 | Text( 58 | 'Just swipe the todo from any direction.', 59 | style: bodyText1, 60 | ), 61 | const Divider(height: 16), 62 | Text( 63 | 'How to pin a todo', 64 | style: headline6, 65 | ), 66 | Text( 67 | 'Tap on the pin icon button trailing the todo name. It will be pinned to the Pinned tab.', 68 | style: bodyText1, 69 | ), 70 | const Divider(height: 16), 71 | Text( 72 | 'How to edit a todo?', 73 | style: headline6, 74 | ), 75 | Text( 76 | 'Just tap on the todo title and type whatever you want.', 77 | style: bodyText1, 78 | ), 79 | const Divider(height: 16), 80 | Text( 81 | 'How to take backup of my todos?', 82 | style: headline6, 83 | ), 84 | Text( 85 | 'Go to \'Backup and Restore\' page and just tap the big \'Create Backup\' button. Your backups will be saved on this folder "/Android/data/$appName/files/". Don\'t forget to give the storage permission to app.', 86 | style: bodyText1, 87 | ), 88 | const Divider(height: 16), 89 | Text( 90 | 'How to restore backup?', 91 | style: headline6, 92 | ), 93 | Text( 94 | 'Go to \'Backup and Restore\' page and then tap the big \'List of backup files\' button. You will be navigated to the \'Backup List\' page where you will find all the backups created by time and date. Tap on the backup file title and then tap on the \'Restore\'. Your data is now restored.', 95 | style: bodyText1, 96 | ), 97 | const Divider(height: 16), 98 | Text( 99 | 'How to share backup file?', 100 | style: headline6, 101 | ), 102 | Text( 103 | 'Go to \'Backup and Restore\' page and then tap the big \'List of backup files\' button. You will be navigated to the \'Backup List\' page where you will find all the backups created by time and date. Tap the share icon next to backup file title. Share wherever you want to share in json file format.', 104 | style: bodyText1, 105 | ), 106 | const Divider(height: 16), 107 | Text( 108 | 'How to restore backup file from storage?', 109 | style: headline6, 110 | ), 111 | Text( 112 | 'Go to \'Backup and Restore\' page and just tap the big \'Restore backup from storage\' button. Then, just choose a valid json file and you\'re done.', 113 | style: bodyText1, 114 | ), 115 | const Divider(height: 16), 116 | Text( 117 | 'Where can I find my created backup files in storage?', 118 | style: headline6, 119 | ), 120 | Text( 121 | 'Go to this location in your file manager "/Android/data/$appName/files/". You will find all the created backup files in this folder.', 122 | style: bodyText1, 123 | ), 124 | ], 125 | ), 126 | ), 127 | ), 128 | ); 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /lib/utils/styles/task_app_theme.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class TaskAppTheme { 4 | // 1 5 | static TextTheme lightTextTheme = const TextTheme( 6 | labelLarge: TextStyle( 7 | fontSize: 18.0, fontWeight: FontWeight.w400, color: Colors.black), 8 | bodyLarge: TextStyle( 9 | fontSize: 18.0, fontWeight: FontWeight.w400, color: Colors.black), 10 | bodyMedium: TextStyle( 11 | fontSize: 16.0, fontWeight: FontWeight.w400, color: Colors.black), 12 | displayLarge: TextStyle( 13 | fontSize: 40.0, fontWeight: FontWeight.w500, color: Colors.black), 14 | displayMedium: TextStyle( 15 | fontSize: 32.0, fontWeight: FontWeight.w400, color: Colors.black), 16 | ); 17 | 18 | // 2 19 | static TextTheme darkTextTheme = const TextTheme( 20 | labelLarge: TextStyle( 21 | fontSize: 18.0, fontWeight: FontWeight.w400, color: Colors.white), 22 | bodyLarge: TextStyle( 23 | fontSize: 18.0, fontWeight: FontWeight.w400, color: Colors.white), 24 | bodyMedium: TextStyle( 25 | fontSize: 16.0, fontWeight: FontWeight.w400, color: Colors.white), 26 | displayLarge: TextStyle( 27 | fontSize: 40.0, fontWeight: FontWeight.w500, color: Colors.white), 28 | displayMedium: TextStyle( 29 | fontSize: 32.0, fontWeight: FontWeight.w400, color: Colors.white), 30 | ); 31 | 32 | // 3 33 | static light() { 34 | return ThemeData( 35 | useMaterial3: true, 36 | textTheme: lightTextTheme, 37 | fontFamily: 'Rubik', 38 | textSelectionTheme: 39 | const TextSelectionThemeData(selectionColor: Colors.green), 40 | primaryColor: Colors.white, 41 | colorScheme: const ColorScheme.light().copyWith( 42 | primary: Colors.white, 43 | secondary: Colors.black87, 44 | brightness: Brightness.light, 45 | ), 46 | textButtonTheme: TextButtonThemeData( 47 | style: ButtonStyle( 48 | foregroundColor: MaterialStateProperty.all(Colors.black87), 49 | overlayColor: MaterialStateProperty.all( 50 | Colors.black54, 51 | ), 52 | )), 53 | appBarTheme: const AppBarTheme( 54 | backgroundColor: Colors.white, 55 | foregroundColor: Colors.black87, 56 | elevation: 0, 57 | centerTitle: true, 58 | toolbarHeight: 64, 59 | ), 60 | inputDecorationTheme: const InputDecorationTheme( 61 | labelStyle: TextStyle(color: Colors.black45), 62 | focusedBorder: OutlineInputBorder( 63 | borderSide: BorderSide( 64 | style: BorderStyle.solid, 65 | width: 2.0, 66 | color: Colors.black87, 67 | ), 68 | borderRadius: BorderRadius.all( 69 | Radius.circular(8.0), 70 | ), 71 | ), 72 | enabledBorder: OutlineInputBorder( 73 | borderSide: BorderSide( 74 | style: BorderStyle.solid, 75 | width: 2.0, 76 | color: Colors.black87, 77 | ), 78 | borderRadius: BorderRadius.all( 79 | Radius.circular(8.0), 80 | ), 81 | ), 82 | border: OutlineInputBorder( 83 | borderSide: BorderSide( 84 | style: BorderStyle.solid, 85 | width: 1.0, 86 | color: Colors.black87, 87 | ), 88 | borderRadius: BorderRadius.all( 89 | Radius.circular(8.0), 90 | ), 91 | ), 92 | ), 93 | ); 94 | } 95 | 96 | // 4 97 | static dark() { 98 | return ThemeData( 99 | textTheme: darkTextTheme, 100 | fontFamily: 'Rubik', 101 | colorScheme: const ColorScheme.dark().copyWith( 102 | primary: Colors.grey[900], 103 | secondary: Colors.white, 104 | brightness: Brightness.dark, 105 | ), 106 | appBarTheme: AppBarTheme( 107 | backgroundColor: Colors.grey[900], 108 | foregroundColor: Colors.white, 109 | elevation: 0, 110 | centerTitle: true, 111 | toolbarHeight: 64, 112 | ), 113 | textButtonTheme: TextButtonThemeData( 114 | style: ButtonStyle( 115 | foregroundColor: MaterialStateProperty.all(Colors.white), 116 | overlayColor: MaterialStateProperty.all( 117 | Colors.white54, 118 | ), 119 | )), 120 | inputDecorationTheme: const InputDecorationTheme( 121 | labelStyle: TextStyle(color: Colors.white54), 122 | focusedBorder: OutlineInputBorder( 123 | borderSide: BorderSide( 124 | style: BorderStyle.solid, 125 | width: 2.0, 126 | color: Colors.white54, 127 | ), 128 | borderRadius: BorderRadius.all( 129 | Radius.circular(8.0), 130 | ), 131 | ), 132 | enabledBorder: OutlineInputBorder( 133 | borderSide: BorderSide( 134 | style: BorderStyle.solid, 135 | width: 2.0, 136 | color: Colors.white54, 137 | ), 138 | borderRadius: BorderRadius.all( 139 | Radius.circular(8.0), 140 | ), 141 | ), 142 | border: OutlineInputBorder( 143 | borderSide: BorderSide( 144 | style: BorderStyle.solid, 145 | width: 1.0, 146 | color: Colors.white54, 147 | ), 148 | borderRadius: BorderRadius.all( 149 | Radius.circular(8.0), 150 | ), 151 | ), 152 | ), 153 | ); 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /lib/ui/pages/home_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:backdrop/backdrop.dart'; 2 | import 'package:carbon_icons/carbon_icons.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter_hooks/flutter_hooks.dart'; 5 | import 'package:flutter_riverpod_todo_app/utils/assets.dart'; 6 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 7 | 8 | import '../../providers/export_providers.dart'; 9 | import '../../utils/hooks/scroll_controller_hook.dart'; 10 | import '../../utils/unique_keys.dart'; 11 | import '../widgets/app_title_with_transition.dart'; 12 | import '../widgets/todo_item.dart'; 13 | import '../widgets/toolbar.dart'; 14 | import 'back_layer_page.dart'; 15 | 16 | class Home extends StatefulHookConsumerWidget { 17 | const Home({Key? key}) : super(key: key); 18 | 19 | @override 20 | HomeState createState() => HomeState(); 21 | } 22 | 23 | class HomeState extends ConsumerState { 24 | @override 25 | void initState() { 26 | super.initState(); 27 | Future(() { 28 | ref.read(todoListProvider.notifier).loadData(); 29 | }); 30 | } 31 | 32 | @override 33 | Widget build(BuildContext context) { 34 | // void _toggleDarkMode() { 35 | // ref.read(isDarkProvider.notifier).toggleTheme(); 36 | // } 37 | 38 | AnimationController mainTitleAnimController = useAnimationController( 39 | duration: kThemeAnimationDuration, initialValue: 1); 40 | AnimationController appbarTitleAnimController = useAnimationController( 41 | duration: kThemeAnimationDuration, initialValue: 0); 42 | ScrollController scrollController = useControllerForAnimation( 43 | mainTitleAnimController, appbarTitleAnimController); 44 | final todos = ref.watch(filteredTodos); 45 | final newTodoController = useTextEditingController(); 46 | final TextTheme textTheme = Theme.of(context).textTheme; 47 | final bool isDark = ref.watch(isDarkProvider).getTheme(); 48 | 49 | return GestureDetector( 50 | onTap: () => FocusScope.of(context).unfocus(), 51 | child: BackdropScaffold( 52 | frontLayerBackgroundColor: Theme.of(context).colorScheme.primary, 53 | backLayerBackgroundColor: Theme.of(context).colorScheme.primary, 54 | headerHeight: 0, 55 | frontLayerBorderRadius: BorderRadius.circular(0), 56 | stickyFrontLayer: true, 57 | frontLayerScrim: isDark ? Colors.black54 : Colors.white60, 58 | backLayerScrim: isDark ? Colors.white54 : Colors.black54, 59 | // backgroundColor: Theme.of(context).colorScheme.primary, 60 | appBar: BackdropAppBar( 61 | centerTitle: true, 62 | title: AppTitleWithTransition( 63 | appbarTitleAnimController, 64 | ), 65 | leading: const Padding( 66 | padding: EdgeInsets.symmetric(horizontal: 16.0), 67 | child: Icon( 68 | CarbonIcons.task, 69 | size: 32, 70 | ), 71 | ), 72 | actions: [ 73 | BackdropToggleButton( 74 | color: isDark ? Colors.white : Colors.black, 75 | icon: AnimatedIcons.close_menu, 76 | ), 77 | const SizedBox( 78 | width: 16, 79 | ) 80 | ], 81 | ), 82 | backLayer: const BackLayerPage(), 83 | frontLayer: ListView( 84 | controller: scrollController, 85 | physics: const BouncingScrollPhysics(), 86 | padding: const EdgeInsets.fromLTRB(16, 0, 16, 16), 87 | children: [ 88 | AppTitleWithTransition( 89 | mainTitleAnimController, 90 | fontSize: textTheme.displayLarge?.fontSize, 91 | ), 92 | const SizedBox( 93 | height: 8, 94 | ), 95 | TextField( 96 | key: addTodoKey, 97 | controller: newTodoController, 98 | cursorColor: Theme.of(context).colorScheme.secondary, 99 | decoration: 100 | const InputDecoration(labelText: 'What needs to be done?'), 101 | onSubmitted: (value) { 102 | ref.read(todoListProvider.notifier).add(value); 103 | newTodoController.clear(); 104 | }, 105 | ), 106 | Padding( 107 | padding: const EdgeInsets.symmetric(vertical: 8.0), 108 | child: Text( 109 | '${ref.watch(uncompletedTodoCount).toString()} of ${ref.watch(totalTodoCount).toString()} items left', 110 | textAlign: TextAlign.center, 111 | style: Theme.of(context).textTheme.bodyMedium, 112 | ), 113 | ), 114 | ToolBar(filter: ref.watch(todoFilterType.state)), 115 | if (todos.data.isEmpty) ...[ 116 | const SizedBox( 117 | height: 24, 118 | ), 119 | Image.asset( 120 | Assets.noTask, 121 | ), 122 | const SizedBox( 123 | height: 24, 124 | ), 125 | const Text( 126 | 'No Task available!\n Try to add a new one.', 127 | textAlign: TextAlign.center, 128 | ), 129 | ], 130 | /// ReorderableListView.builder is used to reorder the list of todos 131 | /// using drag and drop. 132 | ReorderableListView.builder( 133 | physics: const NeverScrollableScrollPhysics(), 134 | header: null, 135 | shrinkWrap: true, 136 | itemCount: todos.data.length, 137 | itemBuilder: (context, i) { 138 | return Container( 139 | key: ValueKey(todos.data[i].id), 140 | decoration: BoxDecoration( 141 | color: Colors.red, 142 | borderRadius: BorderRadius.circular(4), 143 | ), 144 | child: Dismissible( 145 | key: ValueKey(todos.data[i].id), 146 | onDismissed: (_) { 147 | ref 148 | .read(todoListProvider.notifier) 149 | .remove(todos.data[i]); 150 | ScaffoldMessenger.of(context) 151 | ..hideCurrentSnackBar() 152 | ..showSnackBar( 153 | const SnackBar( 154 | content: Text("Task Deleted!"), 155 | backgroundColor: Colors.red, 156 | duration: Duration(seconds: 2), 157 | ), 158 | ); 159 | }, 160 | child: ProviderScope( 161 | overrides: [ 162 | if (ref.watch(totalTodoCount) != 0) 163 | currentTodo.overrideWithValue(todos.data[i]) 164 | ], 165 | child: const TodoItem(), 166 | ), 167 | ), 168 | ); 169 | }, 170 | onReorder: (oldIndex, newIndex) => ref 171 | .read(todoListProvider.notifier) 172 | .reorder(oldIndex, newIndex)), 173 | ], 174 | ), 175 | ), 176 | ); 177 | } 178 | } 179 | 180 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 11 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 12 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 13 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 14 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 15 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; 16 | /* End PBXBuildFile section */ 17 | 18 | /* Begin PBXCopyFilesBuildPhase section */ 19 | 9705A1C41CF9048500538489 /* Embed Frameworks */ = { 20 | isa = PBXCopyFilesBuildPhase; 21 | buildActionMask = 2147483647; 22 | dstPath = ""; 23 | dstSubfolderSpec = 10; 24 | files = ( 25 | ); 26 | name = "Embed Frameworks"; 27 | runOnlyForDeploymentPostprocessing = 0; 28 | }; 29 | /* End PBXCopyFilesBuildPhase section */ 30 | 31 | /* Begin PBXFileReference section */ 32 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 33 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 34 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 35 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 36 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 37 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 38 | 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 39 | 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 40 | 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; 41 | 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 42 | 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 43 | 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 44 | 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 45 | /* End PBXFileReference section */ 46 | 47 | /* Begin PBXFrameworksBuildPhase section */ 48 | 97C146EB1CF9000F007C117D /* Frameworks */ = { 49 | isa = PBXFrameworksBuildPhase; 50 | buildActionMask = 2147483647; 51 | files = ( 52 | ); 53 | runOnlyForDeploymentPostprocessing = 0; 54 | }; 55 | /* End PBXFrameworksBuildPhase section */ 56 | 57 | /* Begin PBXGroup section */ 58 | 9740EEB11CF90186004384FC /* Flutter */ = { 59 | isa = PBXGroup; 60 | children = ( 61 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, 62 | 9740EEB21CF90195004384FC /* Debug.xcconfig */, 63 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, 64 | 9740EEB31CF90195004384FC /* Generated.xcconfig */, 65 | ); 66 | name = Flutter; 67 | sourceTree = ""; 68 | }; 69 | 97C146E51CF9000F007C117D = { 70 | isa = PBXGroup; 71 | children = ( 72 | 9740EEB11CF90186004384FC /* Flutter */, 73 | 97C146F01CF9000F007C117D /* Runner */, 74 | 97C146EF1CF9000F007C117D /* Products */, 75 | ); 76 | sourceTree = ""; 77 | }; 78 | 97C146EF1CF9000F007C117D /* Products */ = { 79 | isa = PBXGroup; 80 | children = ( 81 | 97C146EE1CF9000F007C117D /* Runner.app */, 82 | ); 83 | name = Products; 84 | sourceTree = ""; 85 | }; 86 | 97C146F01CF9000F007C117D /* Runner */ = { 87 | isa = PBXGroup; 88 | children = ( 89 | 97C146FA1CF9000F007C117D /* Main.storyboard */, 90 | 97C146FD1CF9000F007C117D /* Assets.xcassets */, 91 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, 92 | 97C147021CF9000F007C117D /* Info.plist */, 93 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, 94 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, 95 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, 96 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, 97 | ); 98 | path = Runner; 99 | sourceTree = ""; 100 | }; 101 | /* End PBXGroup section */ 102 | 103 | /* Begin PBXNativeTarget section */ 104 | 97C146ED1CF9000F007C117D /* Runner */ = { 105 | isa = PBXNativeTarget; 106 | buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; 107 | buildPhases = ( 108 | 9740EEB61CF901F6004384FC /* Run Script */, 109 | 97C146EA1CF9000F007C117D /* Sources */, 110 | 97C146EB1CF9000F007C117D /* Frameworks */, 111 | 97C146EC1CF9000F007C117D /* Resources */, 112 | 9705A1C41CF9048500538489 /* Embed Frameworks */, 113 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */, 114 | ); 115 | buildRules = ( 116 | ); 117 | dependencies = ( 118 | ); 119 | name = Runner; 120 | productName = Runner; 121 | productReference = 97C146EE1CF9000F007C117D /* Runner.app */; 122 | productType = "com.apple.product-type.application"; 123 | }; 124 | /* End PBXNativeTarget section */ 125 | 126 | /* Begin PBXProject section */ 127 | 97C146E61CF9000F007C117D /* Project object */ = { 128 | isa = PBXProject; 129 | attributes = { 130 | LastUpgradeCheck = 1020; 131 | ORGANIZATIONNAME = ""; 132 | TargetAttributes = { 133 | 97C146ED1CF9000F007C117D = { 134 | CreatedOnToolsVersion = 7.3.1; 135 | LastSwiftMigration = 1100; 136 | }; 137 | }; 138 | }; 139 | buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; 140 | compatibilityVersion = "Xcode 9.3"; 141 | developmentRegion = en; 142 | hasScannedForEncodings = 0; 143 | knownRegions = ( 144 | en, 145 | Base, 146 | ); 147 | mainGroup = 97C146E51CF9000F007C117D; 148 | productRefGroup = 97C146EF1CF9000F007C117D /* Products */; 149 | projectDirPath = ""; 150 | projectRoot = ""; 151 | targets = ( 152 | 97C146ED1CF9000F007C117D /* Runner */, 153 | ); 154 | }; 155 | /* End PBXProject section */ 156 | 157 | /* Begin PBXResourcesBuildPhase section */ 158 | 97C146EC1CF9000F007C117D /* Resources */ = { 159 | isa = PBXResourcesBuildPhase; 160 | buildActionMask = 2147483647; 161 | files = ( 162 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, 163 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, 164 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, 165 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, 166 | ); 167 | runOnlyForDeploymentPostprocessing = 0; 168 | }; 169 | /* End PBXResourcesBuildPhase section */ 170 | 171 | /* Begin PBXShellScriptBuildPhase section */ 172 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { 173 | isa = PBXShellScriptBuildPhase; 174 | buildActionMask = 2147483647; 175 | files = ( 176 | ); 177 | inputPaths = ( 178 | ); 179 | name = "Thin Binary"; 180 | outputPaths = ( 181 | ); 182 | runOnlyForDeploymentPostprocessing = 0; 183 | shellPath = /bin/sh; 184 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; 185 | }; 186 | 9740EEB61CF901F6004384FC /* Run Script */ = { 187 | isa = PBXShellScriptBuildPhase; 188 | buildActionMask = 2147483647; 189 | files = ( 190 | ); 191 | inputPaths = ( 192 | ); 193 | name = "Run Script"; 194 | outputPaths = ( 195 | ); 196 | runOnlyForDeploymentPostprocessing = 0; 197 | shellPath = /bin/sh; 198 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; 199 | }; 200 | /* End PBXShellScriptBuildPhase section */ 201 | 202 | /* Begin PBXSourcesBuildPhase section */ 203 | 97C146EA1CF9000F007C117D /* Sources */ = { 204 | isa = PBXSourcesBuildPhase; 205 | buildActionMask = 2147483647; 206 | files = ( 207 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, 208 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, 209 | ); 210 | runOnlyForDeploymentPostprocessing = 0; 211 | }; 212 | /* End PBXSourcesBuildPhase section */ 213 | 214 | /* Begin PBXVariantGroup section */ 215 | 97C146FA1CF9000F007C117D /* Main.storyboard */ = { 216 | isa = PBXVariantGroup; 217 | children = ( 218 | 97C146FB1CF9000F007C117D /* Base */, 219 | ); 220 | name = Main.storyboard; 221 | sourceTree = ""; 222 | }; 223 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { 224 | isa = PBXVariantGroup; 225 | children = ( 226 | 97C147001CF9000F007C117D /* Base */, 227 | ); 228 | name = LaunchScreen.storyboard; 229 | sourceTree = ""; 230 | }; 231 | /* End PBXVariantGroup section */ 232 | 233 | /* Begin XCBuildConfiguration section */ 234 | 249021D3217E4FDB00AE95B9 /* Profile */ = { 235 | isa = XCBuildConfiguration; 236 | buildSettings = { 237 | ALWAYS_SEARCH_USER_PATHS = NO; 238 | CLANG_ANALYZER_NONNULL = YES; 239 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 240 | CLANG_CXX_LIBRARY = "libc++"; 241 | CLANG_ENABLE_MODULES = YES; 242 | CLANG_ENABLE_OBJC_ARC = YES; 243 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 244 | CLANG_WARN_BOOL_CONVERSION = YES; 245 | CLANG_WARN_COMMA = YES; 246 | CLANG_WARN_CONSTANT_CONVERSION = YES; 247 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 248 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 249 | CLANG_WARN_EMPTY_BODY = YES; 250 | CLANG_WARN_ENUM_CONVERSION = YES; 251 | CLANG_WARN_INFINITE_RECURSION = YES; 252 | CLANG_WARN_INT_CONVERSION = YES; 253 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 254 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 255 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 256 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 257 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 258 | CLANG_WARN_STRICT_PROTOTYPES = YES; 259 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 260 | CLANG_WARN_UNREACHABLE_CODE = YES; 261 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 262 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 263 | COPY_PHASE_STRIP = NO; 264 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 265 | ENABLE_NS_ASSERTIONS = NO; 266 | ENABLE_STRICT_OBJC_MSGSEND = YES; 267 | GCC_C_LANGUAGE_STANDARD = gnu99; 268 | GCC_NO_COMMON_BLOCKS = YES; 269 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 270 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 271 | GCC_WARN_UNDECLARED_SELECTOR = YES; 272 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 273 | GCC_WARN_UNUSED_FUNCTION = YES; 274 | GCC_WARN_UNUSED_VARIABLE = YES; 275 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 276 | MTL_ENABLE_DEBUG_INFO = NO; 277 | SDKROOT = iphoneos; 278 | SUPPORTED_PLATFORMS = iphoneos; 279 | TARGETED_DEVICE_FAMILY = "1,2"; 280 | VALIDATE_PRODUCT = YES; 281 | }; 282 | name = Profile; 283 | }; 284 | 249021D4217E4FDB00AE95B9 /* Profile */ = { 285 | isa = XCBuildConfiguration; 286 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 287 | buildSettings = { 288 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 289 | CLANG_ENABLE_MODULES = YES; 290 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 291 | ENABLE_BITCODE = NO; 292 | INFOPLIST_FILE = Runner/Info.plist; 293 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 294 | PRODUCT_BUNDLE_IDENTIFIER = com.example.flutterRiverpodTodoApp; 295 | PRODUCT_NAME = "$(TARGET_NAME)"; 296 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 297 | SWIFT_VERSION = 5.0; 298 | VERSIONING_SYSTEM = "apple-generic"; 299 | }; 300 | name = Profile; 301 | }; 302 | 97C147031CF9000F007C117D /* Debug */ = { 303 | isa = XCBuildConfiguration; 304 | buildSettings = { 305 | ALWAYS_SEARCH_USER_PATHS = NO; 306 | CLANG_ANALYZER_NONNULL = YES; 307 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 308 | CLANG_CXX_LIBRARY = "libc++"; 309 | CLANG_ENABLE_MODULES = YES; 310 | CLANG_ENABLE_OBJC_ARC = YES; 311 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 312 | CLANG_WARN_BOOL_CONVERSION = YES; 313 | CLANG_WARN_COMMA = YES; 314 | CLANG_WARN_CONSTANT_CONVERSION = YES; 315 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 316 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 317 | CLANG_WARN_EMPTY_BODY = YES; 318 | CLANG_WARN_ENUM_CONVERSION = YES; 319 | CLANG_WARN_INFINITE_RECURSION = YES; 320 | CLANG_WARN_INT_CONVERSION = YES; 321 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 322 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 323 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 324 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 325 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 326 | CLANG_WARN_STRICT_PROTOTYPES = YES; 327 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 328 | CLANG_WARN_UNREACHABLE_CODE = YES; 329 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 330 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 331 | COPY_PHASE_STRIP = NO; 332 | DEBUG_INFORMATION_FORMAT = dwarf; 333 | ENABLE_STRICT_OBJC_MSGSEND = YES; 334 | ENABLE_TESTABILITY = YES; 335 | GCC_C_LANGUAGE_STANDARD = gnu99; 336 | GCC_DYNAMIC_NO_PIC = NO; 337 | GCC_NO_COMMON_BLOCKS = YES; 338 | GCC_OPTIMIZATION_LEVEL = 0; 339 | GCC_PREPROCESSOR_DEFINITIONS = ( 340 | "DEBUG=1", 341 | "$(inherited)", 342 | ); 343 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 344 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 345 | GCC_WARN_UNDECLARED_SELECTOR = YES; 346 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 347 | GCC_WARN_UNUSED_FUNCTION = YES; 348 | GCC_WARN_UNUSED_VARIABLE = YES; 349 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 350 | MTL_ENABLE_DEBUG_INFO = YES; 351 | ONLY_ACTIVE_ARCH = YES; 352 | SDKROOT = iphoneos; 353 | TARGETED_DEVICE_FAMILY = "1,2"; 354 | }; 355 | name = Debug; 356 | }; 357 | 97C147041CF9000F007C117D /* Release */ = { 358 | isa = XCBuildConfiguration; 359 | buildSettings = { 360 | ALWAYS_SEARCH_USER_PATHS = NO; 361 | CLANG_ANALYZER_NONNULL = YES; 362 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 363 | CLANG_CXX_LIBRARY = "libc++"; 364 | CLANG_ENABLE_MODULES = YES; 365 | CLANG_ENABLE_OBJC_ARC = YES; 366 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 367 | CLANG_WARN_BOOL_CONVERSION = YES; 368 | CLANG_WARN_COMMA = YES; 369 | CLANG_WARN_CONSTANT_CONVERSION = YES; 370 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 371 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 372 | CLANG_WARN_EMPTY_BODY = YES; 373 | CLANG_WARN_ENUM_CONVERSION = YES; 374 | CLANG_WARN_INFINITE_RECURSION = YES; 375 | CLANG_WARN_INT_CONVERSION = YES; 376 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 377 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 378 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 379 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 380 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 381 | CLANG_WARN_STRICT_PROTOTYPES = YES; 382 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 383 | CLANG_WARN_UNREACHABLE_CODE = YES; 384 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 385 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 386 | COPY_PHASE_STRIP = NO; 387 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 388 | ENABLE_NS_ASSERTIONS = NO; 389 | ENABLE_STRICT_OBJC_MSGSEND = YES; 390 | GCC_C_LANGUAGE_STANDARD = gnu99; 391 | GCC_NO_COMMON_BLOCKS = YES; 392 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 393 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 394 | GCC_WARN_UNDECLARED_SELECTOR = YES; 395 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 396 | GCC_WARN_UNUSED_FUNCTION = YES; 397 | GCC_WARN_UNUSED_VARIABLE = YES; 398 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 399 | MTL_ENABLE_DEBUG_INFO = NO; 400 | SDKROOT = iphoneos; 401 | SUPPORTED_PLATFORMS = iphoneos; 402 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 403 | TARGETED_DEVICE_FAMILY = "1,2"; 404 | VALIDATE_PRODUCT = YES; 405 | }; 406 | name = Release; 407 | }; 408 | 97C147061CF9000F007C117D /* Debug */ = { 409 | isa = XCBuildConfiguration; 410 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; 411 | buildSettings = { 412 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 413 | CLANG_ENABLE_MODULES = YES; 414 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 415 | ENABLE_BITCODE = NO; 416 | INFOPLIST_FILE = Runner/Info.plist; 417 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 418 | PRODUCT_BUNDLE_IDENTIFIER = com.example.flutterRiverpodTodoApp; 419 | PRODUCT_NAME = "$(TARGET_NAME)"; 420 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 421 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 422 | SWIFT_VERSION = 5.0; 423 | VERSIONING_SYSTEM = "apple-generic"; 424 | }; 425 | name = Debug; 426 | }; 427 | 97C147071CF9000F007C117D /* Release */ = { 428 | isa = XCBuildConfiguration; 429 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 430 | buildSettings = { 431 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 432 | CLANG_ENABLE_MODULES = YES; 433 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 434 | ENABLE_BITCODE = NO; 435 | INFOPLIST_FILE = Runner/Info.plist; 436 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 437 | PRODUCT_BUNDLE_IDENTIFIER = com.example.flutterRiverpodTodoApp; 438 | PRODUCT_NAME = "$(TARGET_NAME)"; 439 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 440 | SWIFT_VERSION = 5.0; 441 | VERSIONING_SYSTEM = "apple-generic"; 442 | }; 443 | name = Release; 444 | }; 445 | /* End XCBuildConfiguration section */ 446 | 447 | /* Begin XCConfigurationList section */ 448 | 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { 449 | isa = XCConfigurationList; 450 | buildConfigurations = ( 451 | 97C147031CF9000F007C117D /* Debug */, 452 | 97C147041CF9000F007C117D /* Release */, 453 | 249021D3217E4FDB00AE95B9 /* Profile */, 454 | ); 455 | defaultConfigurationIsVisible = 0; 456 | defaultConfigurationName = Release; 457 | }; 458 | 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { 459 | isa = XCConfigurationList; 460 | buildConfigurations = ( 461 | 97C147061CF9000F007C117D /* Debug */, 462 | 97C147071CF9000F007C117D /* Release */, 463 | 249021D4217E4FDB00AE95B9 /* Profile */, 464 | ); 465 | defaultConfigurationIsVisible = 0; 466 | defaultConfigurationName = Release; 467 | }; 468 | /* End XCConfigurationList section */ 469 | }; 470 | rootObject = 97C146E61CF9000F007C117D /* Project object */; 471 | } 472 | -------------------------------------------------------------------------------- /pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | async: 5 | dependency: transitive 6 | description: 7 | name: async 8 | sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" 9 | url: "https://pub.dev" 10 | source: hosted 11 | version: "2.11.0" 12 | backdrop: 13 | dependency: "direct main" 14 | description: 15 | name: backdrop 16 | sha256: cb7450b465b638835cf5908ee96785dd7d324029beb96fa7a7b6d81216610cda 17 | url: "https://pub.dev" 18 | source: hosted 19 | version: "0.9.1" 20 | boolean_selector: 21 | dependency: transitive 22 | description: 23 | name: boolean_selector 24 | sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" 25 | url: "https://pub.dev" 26 | source: hosted 27 | version: "2.1.1" 28 | carbon_icons: 29 | dependency: "direct main" 30 | description: 31 | name: carbon_icons 32 | sha256: f962b7cbfe88a6229656cb3ef77afe33e69e5702a70a556352f940d82d5be2d8 33 | url: "https://pub.dev" 34 | source: hosted 35 | version: "0.0.1+2" 36 | characters: 37 | dependency: transitive 38 | description: 39 | name: characters 40 | sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" 41 | url: "https://pub.dev" 42 | source: hosted 43 | version: "1.3.0" 44 | clock: 45 | dependency: transitive 46 | description: 47 | name: clock 48 | sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf 49 | url: "https://pub.dev" 50 | source: hosted 51 | version: "1.1.1" 52 | collection: 53 | dependency: transitive 54 | description: 55 | name: collection 56 | sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a 57 | url: "https://pub.dev" 58 | source: hosted 59 | version: "1.18.0" 60 | cross_file: 61 | dependency: transitive 62 | description: 63 | name: cross_file 64 | sha256: "55d7b444feb71301ef6b8838dbc1ae02e63dd48c8773f3810ff53bb1e2945b32" 65 | url: "https://pub.dev" 66 | source: hosted 67 | version: "0.3.4+1" 68 | crypto: 69 | dependency: transitive 70 | description: 71 | name: crypto 72 | sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab 73 | url: "https://pub.dev" 74 | source: hosted 75 | version: "3.0.3" 76 | cupertino_icons: 77 | dependency: "direct main" 78 | description: 79 | name: cupertino_icons 80 | sha256: d57953e10f9f8327ce64a508a355f0b1ec902193f66288e8cb5070e7c47eeb2d 81 | url: "https://pub.dev" 82 | source: hosted 83 | version: "1.0.6" 84 | fake_async: 85 | dependency: transitive 86 | description: 87 | name: fake_async 88 | sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" 89 | url: "https://pub.dev" 90 | source: hosted 91 | version: "1.3.1" 92 | ffi: 93 | dependency: transitive 94 | description: 95 | name: ffi 96 | sha256: "493f37e7df1804778ff3a53bd691d8692ddf69702cf4c1c1096a2e41b4779e21" 97 | url: "https://pub.dev" 98 | source: hosted 99 | version: "2.1.2" 100 | file: 101 | dependency: transitive 102 | description: 103 | name: file 104 | sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c" 105 | url: "https://pub.dev" 106 | source: hosted 107 | version: "7.0.0" 108 | file_picker: 109 | dependency: "direct main" 110 | description: 111 | name: file_picker 112 | sha256: "1bbf65dd997458a08b531042ec3794112a6c39c07c37ff22113d2e7e4f81d4e4" 113 | url: "https://pub.dev" 114 | source: hosted 115 | version: "6.2.1" 116 | fixnum: 117 | dependency: transitive 118 | description: 119 | name: fixnum 120 | sha256: "25517a4deb0c03aa0f32fd12db525856438902d9c16536311e76cdc57b31d7d1" 121 | url: "https://pub.dev" 122 | source: hosted 123 | version: "1.1.0" 124 | flutter: 125 | dependency: "direct main" 126 | description: flutter 127 | source: sdk 128 | version: "0.0.0" 129 | flutter_hooks: 130 | dependency: "direct main" 131 | description: 132 | name: flutter_hooks 133 | sha256: cde36b12f7188c85286fba9b38cc5a902e7279f36dd676967106c041dc9dde70 134 | url: "https://pub.dev" 135 | source: hosted 136 | version: "0.20.5" 137 | flutter_lints: 138 | dependency: "direct dev" 139 | description: 140 | name: flutter_lints 141 | sha256: b543301ad291598523947dc534aaddc5aaad597b709d2426d3a0e0d44c5cb493 142 | url: "https://pub.dev" 143 | source: hosted 144 | version: "1.0.4" 145 | flutter_plugin_android_lifecycle: 146 | dependency: transitive 147 | description: 148 | name: flutter_plugin_android_lifecycle 149 | sha256: b068ffc46f82a55844acfa4fdbb61fad72fa2aef0905548419d97f0f95c456da 150 | url: "https://pub.dev" 151 | source: hosted 152 | version: "2.0.17" 153 | flutter_riverpod: 154 | dependency: transitive 155 | description: 156 | name: flutter_riverpod 157 | sha256: "0f1974eff5bbe774bf1d870e406fc6f29e3d6f1c46bd9c58e7172ff68a785d7d" 158 | url: "https://pub.dev" 159 | source: hosted 160 | version: "2.5.1" 161 | flutter_test: 162 | dependency: "direct dev" 163 | description: flutter 164 | source: sdk 165 | version: "0.0.0" 166 | flutter_web_plugins: 167 | dependency: transitive 168 | description: flutter 169 | source: sdk 170 | version: "0.0.0" 171 | hooks_riverpod: 172 | dependency: "direct main" 173 | description: 174 | name: hooks_riverpod 175 | sha256: "45b2030a18bcd6dbd680c2c91bc3b33e3fe7c323e3acb5ecec93a613e2fbaa8a" 176 | url: "https://pub.dev" 177 | source: hosted 178 | version: "2.5.1" 179 | leak_tracker: 180 | dependency: transitive 181 | description: 182 | name: leak_tracker 183 | sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa" 184 | url: "https://pub.dev" 185 | source: hosted 186 | version: "10.0.0" 187 | leak_tracker_flutter_testing: 188 | dependency: transitive 189 | description: 190 | name: leak_tracker_flutter_testing 191 | sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0 192 | url: "https://pub.dev" 193 | source: hosted 194 | version: "2.0.1" 195 | leak_tracker_testing: 196 | dependency: transitive 197 | description: 198 | name: leak_tracker_testing 199 | sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47 200 | url: "https://pub.dev" 201 | source: hosted 202 | version: "2.0.1" 203 | lints: 204 | dependency: transitive 205 | description: 206 | name: lints 207 | sha256: a2c3d198cb5ea2e179926622d433331d8b58374ab8f29cdda6e863bd62fd369c 208 | url: "https://pub.dev" 209 | source: hosted 210 | version: "1.0.1" 211 | matcher: 212 | dependency: transitive 213 | description: 214 | name: matcher 215 | sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb 216 | url: "https://pub.dev" 217 | source: hosted 218 | version: "0.12.16+1" 219 | material_color_utilities: 220 | dependency: transitive 221 | description: 222 | name: material_color_utilities 223 | sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" 224 | url: "https://pub.dev" 225 | source: hosted 226 | version: "0.8.0" 227 | meta: 228 | dependency: transitive 229 | description: 230 | name: meta 231 | sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 232 | url: "https://pub.dev" 233 | source: hosted 234 | version: "1.11.0" 235 | mime: 236 | dependency: transitive 237 | description: 238 | name: mime 239 | sha256: "2e123074287cc9fd6c09de8336dae606d1ddb88d9ac47358826db698c176a1f2" 240 | url: "https://pub.dev" 241 | source: hosted 242 | version: "1.0.5" 243 | path: 244 | dependency: transitive 245 | description: 246 | name: path 247 | sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" 248 | url: "https://pub.dev" 249 | source: hosted 250 | version: "1.9.0" 251 | path_provider: 252 | dependency: "direct main" 253 | description: 254 | name: path_provider 255 | sha256: b27217933eeeba8ff24845c34003b003b2b22151de3c908d0e679e8fe1aa078b 256 | url: "https://pub.dev" 257 | source: hosted 258 | version: "2.1.2" 259 | path_provider_android: 260 | dependency: transitive 261 | description: 262 | name: path_provider_android 263 | sha256: "477184d672607c0a3bf68fbbf601805f92ef79c82b64b4d6eb318cbca4c48668" 264 | url: "https://pub.dev" 265 | source: hosted 266 | version: "2.2.2" 267 | path_provider_foundation: 268 | dependency: transitive 269 | description: 270 | name: path_provider_foundation 271 | sha256: "5a7999be66e000916500be4f15a3633ebceb8302719b47b9cc49ce924125350f" 272 | url: "https://pub.dev" 273 | source: hosted 274 | version: "2.3.2" 275 | path_provider_linux: 276 | dependency: transitive 277 | description: 278 | name: path_provider_linux 279 | sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279 280 | url: "https://pub.dev" 281 | source: hosted 282 | version: "2.2.1" 283 | path_provider_platform_interface: 284 | dependency: transitive 285 | description: 286 | name: path_provider_platform_interface 287 | sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334" 288 | url: "https://pub.dev" 289 | source: hosted 290 | version: "2.1.2" 291 | path_provider_windows: 292 | dependency: transitive 293 | description: 294 | name: path_provider_windows 295 | sha256: "8bc9f22eee8690981c22aa7fc602f5c85b497a6fb2ceb35ee5a5e5ed85ad8170" 296 | url: "https://pub.dev" 297 | source: hosted 298 | version: "2.2.1" 299 | permission_handler: 300 | dependency: "direct main" 301 | description: 302 | name: permission_handler 303 | sha256: "74e962b7fad7ff75959161bb2c0ad8fe7f2568ee82621c9c2660b751146bfe44" 304 | url: "https://pub.dev" 305 | source: hosted 306 | version: "11.3.0" 307 | permission_handler_android: 308 | dependency: transitive 309 | description: 310 | name: permission_handler_android 311 | sha256: "1acac6bae58144b442f11e66621c062aead9c99841093c38f5bcdcc24c1c3474" 312 | url: "https://pub.dev" 313 | source: hosted 314 | version: "12.0.5" 315 | permission_handler_apple: 316 | dependency: transitive 317 | description: 318 | name: permission_handler_apple 319 | sha256: "92861b0f0c2443dd8898398c2baa4f1ae925109b5909ae4a17d0108a6a788932" 320 | url: "https://pub.dev" 321 | source: hosted 322 | version: "9.4.2" 323 | permission_handler_html: 324 | dependency: transitive 325 | description: 326 | name: permission_handler_html 327 | sha256: "54bf176b90f6eddd4ece307e2c06cf977fb3973719c35a93b85cc7093eb6070d" 328 | url: "https://pub.dev" 329 | source: hosted 330 | version: "0.1.1" 331 | permission_handler_platform_interface: 332 | dependency: transitive 333 | description: 334 | name: permission_handler_platform_interface 335 | sha256: "23dfba8447c076ab5be3dee9ceb66aad345c4a648f0cac292c77b1eb0e800b78" 336 | url: "https://pub.dev" 337 | source: hosted 338 | version: "4.2.0" 339 | permission_handler_windows: 340 | dependency: transitive 341 | description: 342 | name: permission_handler_windows 343 | sha256: "1a790728016f79a41216d88672dbc5df30e686e811ad4e698bfc51f76ad91f1e" 344 | url: "https://pub.dev" 345 | source: hosted 346 | version: "0.2.1" 347 | platform: 348 | dependency: transitive 349 | description: 350 | name: platform 351 | sha256: "12220bb4b65720483f8fa9450b4332347737cf8213dd2840d8b2c823e47243ec" 352 | url: "https://pub.dev" 353 | source: hosted 354 | version: "3.1.4" 355 | plugin_platform_interface: 356 | dependency: transitive 357 | description: 358 | name: plugin_platform_interface 359 | sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" 360 | url: "https://pub.dev" 361 | source: hosted 362 | version: "2.1.8" 363 | riverpod: 364 | dependency: transitive 365 | description: 366 | name: riverpod 367 | sha256: f21b32ffd26a36555e501b04f4a5dca43ed59e16343f1a30c13632b2351dfa4d 368 | url: "https://pub.dev" 369 | source: hosted 370 | version: "2.5.1" 371 | share_plus: 372 | dependency: "direct main" 373 | description: 374 | name: share_plus 375 | sha256: "3ef39599b00059db0990ca2e30fca0a29d8b37aae924d60063f8e0184cf20900" 376 | url: "https://pub.dev" 377 | source: hosted 378 | version: "7.2.2" 379 | share_plus_platform_interface: 380 | dependency: transitive 381 | description: 382 | name: share_plus_platform_interface 383 | sha256: "251eb156a8b5fa9ce033747d73535bf53911071f8d3b6f4f0b578505ce0d4496" 384 | url: "https://pub.dev" 385 | source: hosted 386 | version: "3.4.0" 387 | shared_preferences: 388 | dependency: "direct main" 389 | description: 390 | name: shared_preferences 391 | sha256: "81429e4481e1ccfb51ede496e916348668fd0921627779233bd24cc3ff6abd02" 392 | url: "https://pub.dev" 393 | source: hosted 394 | version: "2.2.2" 395 | shared_preferences_android: 396 | dependency: transitive 397 | description: 398 | name: shared_preferences_android 399 | sha256: "8568a389334b6e83415b6aae55378e158fbc2314e074983362d20c562780fb06" 400 | url: "https://pub.dev" 401 | source: hosted 402 | version: "2.2.1" 403 | shared_preferences_foundation: 404 | dependency: transitive 405 | description: 406 | name: shared_preferences_foundation 407 | sha256: "7708d83064f38060c7b39db12aefe449cb8cdc031d6062280087bc4cdb988f5c" 408 | url: "https://pub.dev" 409 | source: hosted 410 | version: "2.3.5" 411 | shared_preferences_linux: 412 | dependency: transitive 413 | description: 414 | name: shared_preferences_linux 415 | sha256: "9f2cbcf46d4270ea8be39fa156d86379077c8a5228d9dfdb1164ae0bb93f1faa" 416 | url: "https://pub.dev" 417 | source: hosted 418 | version: "2.3.2" 419 | shared_preferences_platform_interface: 420 | dependency: transitive 421 | description: 422 | name: shared_preferences_platform_interface 423 | sha256: "22e2ecac9419b4246d7c22bfbbda589e3acf5c0351137d87dd2939d984d37c3b" 424 | url: "https://pub.dev" 425 | source: hosted 426 | version: "2.3.2" 427 | shared_preferences_web: 428 | dependency: transitive 429 | description: 430 | name: shared_preferences_web 431 | sha256: "9aee1089b36bd2aafe06582b7d7817fd317ef05fc30e6ba14bff247d0933042a" 432 | url: "https://pub.dev" 433 | source: hosted 434 | version: "2.3.0" 435 | shared_preferences_windows: 436 | dependency: transitive 437 | description: 438 | name: shared_preferences_windows 439 | sha256: "841ad54f3c8381c480d0c9b508b89a34036f512482c407e6df7a9c4aa2ef8f59" 440 | url: "https://pub.dev" 441 | source: hosted 442 | version: "2.3.2" 443 | sky_engine: 444 | dependency: transitive 445 | description: flutter 446 | source: sdk 447 | version: "0.0.99" 448 | source_span: 449 | dependency: transitive 450 | description: 451 | name: source_span 452 | sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" 453 | url: "https://pub.dev" 454 | source: hosted 455 | version: "1.10.0" 456 | sprintf: 457 | dependency: transitive 458 | description: 459 | name: sprintf 460 | sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23" 461 | url: "https://pub.dev" 462 | source: hosted 463 | version: "7.0.0" 464 | stack_trace: 465 | dependency: transitive 466 | description: 467 | name: stack_trace 468 | sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" 469 | url: "https://pub.dev" 470 | source: hosted 471 | version: "1.11.1" 472 | state_notifier: 473 | dependency: transitive 474 | description: 475 | name: state_notifier 476 | sha256: b8677376aa54f2d7c58280d5a007f9e8774f1968d1fb1c096adcb4792fba29bb 477 | url: "https://pub.dev" 478 | source: hosted 479 | version: "1.0.0" 480 | stream_channel: 481 | dependency: transitive 482 | description: 483 | name: stream_channel 484 | sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 485 | url: "https://pub.dev" 486 | source: hosted 487 | version: "2.1.2" 488 | string_scanner: 489 | dependency: transitive 490 | description: 491 | name: string_scanner 492 | sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" 493 | url: "https://pub.dev" 494 | source: hosted 495 | version: "1.2.0" 496 | term_glyph: 497 | dependency: transitive 498 | description: 499 | name: term_glyph 500 | sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 501 | url: "https://pub.dev" 502 | source: hosted 503 | version: "1.2.1" 504 | test_api: 505 | dependency: transitive 506 | description: 507 | name: test_api 508 | sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" 509 | url: "https://pub.dev" 510 | source: hosted 511 | version: "0.6.1" 512 | typed_data: 513 | dependency: transitive 514 | description: 515 | name: typed_data 516 | sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c 517 | url: "https://pub.dev" 518 | source: hosted 519 | version: "1.3.2" 520 | url_launcher: 521 | dependency: "direct main" 522 | description: 523 | name: url_launcher 524 | sha256: "0ecc004c62fd3ed36a2ffcbe0dd9700aee63bd7532d0b642a488b1ec310f492e" 525 | url: "https://pub.dev" 526 | source: hosted 527 | version: "6.2.5" 528 | url_launcher_android: 529 | dependency: transitive 530 | description: 531 | name: url_launcher_android 532 | sha256: d4ed0711849dd8e33eb2dd69c25db0d0d3fdc37e0a62e629fe32f57a22db2745 533 | url: "https://pub.dev" 534 | source: hosted 535 | version: "6.3.0" 536 | url_launcher_ios: 537 | dependency: transitive 538 | description: 539 | name: url_launcher_ios 540 | sha256: "9149d493b075ed740901f3ee844a38a00b33116c7c5c10d7fb27df8987fb51d5" 541 | url: "https://pub.dev" 542 | source: hosted 543 | version: "6.2.5" 544 | url_launcher_linux: 545 | dependency: transitive 546 | description: 547 | name: url_launcher_linux 548 | sha256: ab360eb661f8879369acac07b6bb3ff09d9471155357da8443fd5d3cf7363811 549 | url: "https://pub.dev" 550 | source: hosted 551 | version: "3.1.1" 552 | url_launcher_macos: 553 | dependency: transitive 554 | description: 555 | name: url_launcher_macos 556 | sha256: b7244901ea3cf489c5335bdacda07264a6e960b1c1b1a9f91e4bc371d9e68234 557 | url: "https://pub.dev" 558 | source: hosted 559 | version: "3.1.0" 560 | url_launcher_platform_interface: 561 | dependency: transitive 562 | description: 563 | name: url_launcher_platform_interface 564 | sha256: "552f8a1e663569be95a8190206a38187b531910283c3e982193e4f2733f01029" 565 | url: "https://pub.dev" 566 | source: hosted 567 | version: "2.3.2" 568 | url_launcher_web: 569 | dependency: transitive 570 | description: 571 | name: url_launcher_web 572 | sha256: "3692a459204a33e04bc94f5fb91158faf4f2c8903281ddd82915adecdb1a901d" 573 | url: "https://pub.dev" 574 | source: hosted 575 | version: "2.3.0" 576 | url_launcher_windows: 577 | dependency: transitive 578 | description: 579 | name: url_launcher_windows 580 | sha256: ecf9725510600aa2bb6d7ddabe16357691b6d2805f66216a97d1b881e21beff7 581 | url: "https://pub.dev" 582 | source: hosted 583 | version: "3.1.1" 584 | uuid: 585 | dependency: "direct main" 586 | description: 587 | name: uuid 588 | sha256: cd210a09f7c18cbe5a02511718e0334de6559871052c90a90c0cca46a4aa81c8 589 | url: "https://pub.dev" 590 | source: hosted 591 | version: "4.3.3" 592 | vector_math: 593 | dependency: transitive 594 | description: 595 | name: vector_math 596 | sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" 597 | url: "https://pub.dev" 598 | source: hosted 599 | version: "2.1.4" 600 | vm_service: 601 | dependency: transitive 602 | description: 603 | name: vm_service 604 | sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957 605 | url: "https://pub.dev" 606 | source: hosted 607 | version: "13.0.0" 608 | web: 609 | dependency: transitive 610 | description: 611 | name: web 612 | sha256: "97da13628db363c635202ad97068d47c5b8aa555808e7a9411963c533b449b27" 613 | url: "https://pub.dev" 614 | source: hosted 615 | version: "0.5.1" 616 | win32: 617 | dependency: transitive 618 | description: 619 | name: win32 620 | sha256: "8cb58b45c47dcb42ab3651533626161d6b67a2921917d8d429791f76972b3480" 621 | url: "https://pub.dev" 622 | source: hosted 623 | version: "5.3.0" 624 | xdg_directories: 625 | dependency: transitive 626 | description: 627 | name: xdg_directories 628 | sha256: faea9dee56b520b55a566385b84f2e8de55e7496104adada9962e0bd11bcff1d 629 | url: "https://pub.dev" 630 | source: hosted 631 | version: "1.0.4" 632 | sdks: 633 | dart: ">=3.3.0 <4.0.0" 634 | flutter: ">=3.19.0" --------------------------------------------------------------------------------