├── lib ├── data │ ├── constants.dart │ ├── providers │ │ ├── app_sp_service.dart │ │ ├── login_provider.dart │ │ ├── task_dao_service.dart │ │ └── dio_config_service.dart │ ├── remote │ │ ├── interceptor │ │ │ ├── header_interceptor.dart │ │ │ ├── error_interceptor.dart │ │ │ ├── response_interceptor.dart │ │ │ └── token_interceptor.dart │ │ ├── dio_config.dart │ │ ├── app_response.dart │ │ ├── app_dio.dart │ │ ├── app_exceptions.dart │ │ └── dio_client.dart │ ├── db │ │ ├── task_dao.g.dart │ │ ├── task_database.dart │ │ └── task_dao.dart │ ├── model │ │ ├── priority.dart │ │ ├── task_model.dart │ │ └── login_bean.dart │ ├── repositories │ │ ├── login_repository.dart │ │ └── task_repository.dart │ ├── local │ │ └── local_login_model_reposity.dart │ └── api │ │ ├── login_api.dart │ │ └── task_api.dart ├── modules │ ├── settings │ │ ├── settings_binding.dart │ │ ├── setting_controller.dart │ │ └── settings_page.dart │ ├── splash │ │ ├── splash_binding.dart │ │ ├── splash_controller.dart │ │ └── splash_page.dart │ ├── task │ │ ├── monthly │ │ │ ├── monthly_page.dart │ │ │ ├── monthly_binding.dart │ │ │ ├── monthly_controller.dart │ │ │ └── components │ │ │ │ └── body.dart │ │ ├── add │ │ │ ├── add_task_page.dart │ │ │ ├── add_task_binding.dart │ │ │ ├── add_task_controller.dart │ │ │ └── components │ │ │ │ └── body.dart │ │ ├── edit │ │ │ ├── edit_task_page.dart │ │ │ ├── edit_task_binding.dart │ │ │ ├── edit_task_controller.dart │ │ │ └── components │ │ │ │ └── body.dart │ │ ├── task │ │ │ ├── task_binding.dart │ │ │ ├── task_page.dart │ │ │ ├── components │ │ │ │ ├── round_check_box.dart │ │ │ │ ├── body.dart │ │ │ │ └── task_widget.dart │ │ │ └── task_controller.dart │ │ └── details │ │ │ └── task_details.dart │ ├── signup │ │ ├── sign_up_page.dart │ │ ├── sign_up_binding.dart │ │ ├── sign_up_controller.dart │ │ └── components │ │ │ └── body.dart │ ├── login │ │ ├── login_page.dart │ │ ├── login_binding.dart │ │ ├── components │ │ │ ├── text_field_container.dart │ │ │ ├── or_divider.dart │ │ │ ├── rounded_input_field.dart │ │ │ ├── round_paaword_field.dart │ │ │ ├── rounded_button.dart │ │ │ ├── alread_have_an_account_check.dart │ │ │ └── body.dart │ │ └── login_controller.dart │ └── profile │ │ └── profile_page.dart ├── utils │ ├── gloab_config.dart │ ├── extension │ │ ├── get_extension.dart │ │ └── date_extension.dart │ └── dependency_injection.dart ├── widgets │ ├── loading_dialog.dart │ └── start_card.dart ├── r.dart ├── routes │ ├── app_routes.dart │ └── app_pages.dart ├── theme │ └── app_theme.dart └── main.dart ├── ios ├── 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 ├── Flutter │ ├── Debug.xcconfig │ ├── Release.xcconfig │ └── AppFrameworkInfo.plist ├── Runner.xcodeproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ ├── WorkspaceSettings.xcsettings │ │ │ └── IDEWorkspaceChecks.plist │ └── xcshareddata │ │ └── xcschemes │ │ └── Runner.xcscheme ├── Runner.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── WorkspaceSettings.xcsettings │ │ └── IDEWorkspaceChecks.plist ├── .gitignore ├── Podfile.lock └── Podfile ├── assets └── images │ └── 3.0x │ ├── getx1.png │ ├── getx2.png │ ├── getx3.png │ ├── getx4.png │ ├── splash.png │ └── profile.jpg ├── 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 │ │ │ │ └── values │ │ │ │ │ └── styles.xml │ │ │ ├── kotlin │ │ │ │ └── com │ │ │ │ │ └── example │ │ │ │ │ └── todo │ │ │ │ │ └── MainActivity.kt │ │ │ └── AndroidManifest.xml │ │ ├── debug │ │ │ └── AndroidManifest.xml │ │ └── profile │ │ │ └── AndroidManifest.xml │ └── build.gradle ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties ├── .gitignore ├── settings.gradle └── build.gradle ├── .metadata ├── .vscode └── launch.json ├── .gitignore ├── test └── widget_test.dart ├── pubspec.yaml ├── README.md └── pubspec.lock /lib/data/constants.dart: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /lib/modules/settings/settings_binding.dart: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/modules/settings/setting_controller.dart: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /assets/images/3.0x/getx1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuexunshi/todo/HEAD/assets/images/3.0x/getx1.png -------------------------------------------------------------------------------- /assets/images/3.0x/getx2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuexunshi/todo/HEAD/assets/images/3.0x/getx2.png -------------------------------------------------------------------------------- /assets/images/3.0x/getx3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuexunshi/todo/HEAD/assets/images/3.0x/getx3.png -------------------------------------------------------------------------------- /assets/images/3.0x/getx4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuexunshi/todo/HEAD/assets/images/3.0x/getx4.png -------------------------------------------------------------------------------- /assets/images/3.0x/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuexunshi/todo/HEAD/assets/images/3.0x/splash.png -------------------------------------------------------------------------------- /assets/images/3.0x/profile.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuexunshi/todo/HEAD/assets/images/3.0x/profile.jpg -------------------------------------------------------------------------------- /lib/utils/gloab_config.dart: -------------------------------------------------------------------------------- 1 | /// 初始化全局配置 2 | class GloabConfig { 3 | static Future init() async {} 4 | } 5 | -------------------------------------------------------------------------------- /ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.useAndroidX=true 3 | android.enableJetifier=true 4 | android.enableR8=true 5 | -------------------------------------------------------------------------------- /ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuexunshi/todo/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/yuexunshi/todo/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/yuexunshi/todo/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/yuexunshi/todo/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/yuexunshi/todo/HEAD/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuexunshi/todo/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuexunshi/todo/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuexunshi/todo/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuexunshi/todo/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/yuexunshi/todo/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/yuexunshi/todo/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/yuexunshi/todo/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/yuexunshi/todo/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/yuexunshi/todo/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/yuexunshi/todo/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/yuexunshi/todo/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/yuexunshi/todo/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/yuexunshi/todo/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/yuexunshi/todo/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/yuexunshi/todo/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/yuexunshi/todo/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuexunshi/todo/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/yuexunshi/todo/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /android/app/src/main/kotlin/com/example/todo/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.example.todo 2 | 3 | import io.flutter.embedding.android.FlutterActivity 4 | 5 | class MainActivity: FlutterActivity() { 6 | } 7 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /lib/modules/splash/splash_binding.dart: -------------------------------------------------------------------------------- 1 | import 'package:get/get.dart'; 2 | 3 | import 'splash_controller.dart'; 4 | 5 | class SplashBinding implements Bindings { 6 | @override 7 | void dependencies() { 8 | Get.lazyPut(() => SplashController()); 9 | } 10 | } 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-5.6.2-all.zip 7 | -------------------------------------------------------------------------------- /lib/data/providers/app_sp_service.dart: -------------------------------------------------------------------------------- 1 | import 'package:get/get.dart'; 2 | import 'package:shared_preferences/shared_preferences.dart'; 3 | 4 | class AppSpController extends GetxService { 5 | Future init() async { 6 | return await SharedPreferences.getInstance(); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /lib/modules/settings/settings_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class MyPage extends StatelessWidget { 4 | @override 5 | Widget build(BuildContext context) { 6 | return Scaffold( 7 | appBar: AppBar(title: Text('MyPage')), 8 | body: Container(), 9 | ); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /.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: 8874f21e79d7ec66d0457c7ab338348e31b17f1d 8 | channel: stable 9 | 10 | project_type: app 11 | -------------------------------------------------------------------------------- /lib/data/remote/interceptor/header_interceptor.dart: -------------------------------------------------------------------------------- 1 | import 'package:dio/dio.dart'; 2 | 3 | class HeaderInterceptors extends InterceptorsWrapper { 4 | @override 5 | onRequest(RequestOptions options) async { 6 | ///超时 7 | options.connectTimeout = 30000; 8 | options.receiveTimeout = 30000; 9 | 10 | return options; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /lib/modules/task/monthly/monthly_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:todo/modules/task/monthly/components/body.dart'; 3 | 4 | class MonthlyPage extends StatelessWidget { 5 | @override 6 | Widget build(BuildContext context) { 7 | return Scaffold(appBar: AppBar(title: Text('Monthly')), body: Body()); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /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. -------------------------------------------------------------------------------- /lib/modules/signup/sign_up_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:get/get.dart'; 3 | import 'package:todo/modules/signup/sign_up_controller.dart'; 4 | import 'components/body.dart'; 5 | 6 | class SignUpPage extends GetView { 7 | @override 8 | Widget build(BuildContext context) { 9 | return Scaffold( 10 | body: Body(), 11 | ); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /lib/utils/extension/get_extension.dart: -------------------------------------------------------------------------------- 1 | import 'package:get/get.dart'; 2 | import 'package:todo/widgets/loading_dialog.dart'; 3 | 4 | extension GetExtension on GetInterface { 5 | dismiss() { 6 | if (Get.isDialogOpen) { 7 | Get.back(); 8 | } 9 | } 10 | 11 | loading() { 12 | if (Get.isDialogOpen) { 13 | Get.back(); 14 | } 15 | Get.dialog(LoadingDialog()); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /lib/data/db/task_dao.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'task_dao.dart'; 4 | 5 | // ************************************************************************** 6 | // DaoGenerator 7 | // ************************************************************************** 8 | 9 | mixin _$TaskDaoMixin on DatabaseAccessor { 10 | $TasksTable get tasks => attachedDatabase.tasks; 11 | } 12 | -------------------------------------------------------------------------------- /lib/modules/task/add/add_task_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'components/body.dart'; 3 | 4 | class AddTaskPage extends StatelessWidget { 5 | @override 6 | Widget build(BuildContext context) { 7 | return Scaffold( 8 | appBar: AppBar(title: Text('Add Task')), 9 | body: Padding( 10 | padding: const EdgeInsets.all(20), 11 | child: Body(), 12 | )); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /lib/modules/task/edit/edit_task_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'components/body.dart'; 3 | 4 | class EditTaskPage extends StatelessWidget { 5 | @override 6 | Widget build(BuildContext context) { 7 | return Scaffold( 8 | appBar: AppBar(title: Text('Edit Task')), 9 | body: Padding( 10 | padding: const EdgeInsets.all(20), 11 | child: Body(), 12 | )); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /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/data/model/priority.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: your name 3 | * @Date: 2020-12-08 20:57:12 4 | * @LastEditTime: 2020-12-08 21:29:43 5 | * @LastEditors: your name 6 | * @Description: In User Settings Edit 7 | * @FilePath: /todo/lib/data/model/priority.dart 8 | */ 9 | import 'package:flutter/material.dart'; 10 | 11 | List prioritiesStr = ['Medium', 'Low', 'High']; 12 | List priorityColor = [Colors.blue, Colors.green, Colors.red]; 13 | List priorities = [0, 1, 2]; 14 | -------------------------------------------------------------------------------- /lib/data/providers/login_provider.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: your name 3 | * @Date: 2020-12-08 20:57:12 4 | * @LastEditTime: 2020-12-11 00:53:29 5 | * @LastEditors: your name 6 | * @Description: In User Settings Edit 7 | * @FilePath: /todo/lib/data/providers/login_provider.dart 8 | */ 9 | import 'package:todo/data/local/local_login_model_reposity.dart'; 10 | 11 | class LoginProvider { 12 | bool isLogin() { 13 | return LocalLoginModelRepository.getLoginModel() != null; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /lib/modules/signup/sign_up_binding.dart: -------------------------------------------------------------------------------- 1 | import 'package:get/get.dart'; 2 | import 'package:todo/data/api/login_api.dart'; 3 | import 'package:todo/data/repositories/login_repository.dart'; 4 | 5 | import 'sign_up_controller.dart'; 6 | 7 | class SiginUpBinding implements Bindings { 8 | @override 9 | void dependencies() { 10 | Get.lazyPut(() => LoginApi()); 11 | Get.lazyPut(() => LoginRepository()); 12 | Get.lazyPut(() => SignUpController()); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /lib/modules/login/login_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:get/get.dart'; 3 | import 'package:todo/modules/login/components/body.dart'; 4 | import 'package:todo/modules/login/login_controller.dart'; 5 | 6 | class LoginPage extends StatelessWidget { 7 | @override 8 | Widget build(BuildContext context) { 9 | return GetBuilder( 10 | builder: (controller) => Scaffold( 11 | body: Body(), 12 | )); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /lib/modules/task/add/add_task_binding.dart: -------------------------------------------------------------------------------- 1 | import 'package:get/get.dart'; 2 | import 'package:todo/data/api/task_api.dart'; 3 | import 'package:todo/data/repositories/task_repository.dart'; 4 | import 'add_task_controller.dart'; 5 | 6 | class AddTaskBinding implements Bindings { 7 | @override 8 | void dependencies() { 9 | Get.lazyPut(() => TaskApi()); 10 | Get.lazyPut(() => TaskRepository()); 11 | Get.lazyPut(() => AddTaskController()); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /lib/modules/task/edit/edit_task_binding.dart: -------------------------------------------------------------------------------- 1 | import 'package:get/get.dart'; 2 | import 'package:todo/data/api/task_api.dart'; 3 | import 'package:todo/data/repositories/task_repository.dart'; 4 | import 'edit_task_controller.dart'; 5 | 6 | class EditTaskBinding implements Bindings { 7 | @override 8 | void dependencies() { 9 | Get.lazyPut(() => TaskApi()); 10 | Get.lazyPut(() => TaskRepository()); 11 | Get.lazyPut(() => EditTaskController()); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // 使用 IntelliSense 了解相关属性。 3 | // 悬停以查看现有属性的描述。 4 | // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "Dart", 9 | "type": "dart", 10 | "request": "launch", 11 | "program": "bin/main.dart" 12 | }, 13 | { 14 | "name": "todo", 15 | "request": "launch", 16 | "type": "dart" 17 | } 18 | ] 19 | } -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /lib/modules/login/login_binding.dart: -------------------------------------------------------------------------------- 1 | import 'package:get/get.dart'; 2 | import 'package:todo/data/api/login_api.dart'; 3 | import 'package:todo/data/repositories/login_repository.dart'; 4 | import 'package:todo/modules/login/login_controller.dart'; 5 | 6 | class LoginPageBinding implements Bindings { 7 | @override 8 | void dependencies() { 9 | Get.lazyPut(() => LoginApi()); 10 | Get.lazyPut(() => LoginRepository()); 11 | Get.lazyPut( 12 | () => LoginController(), 13 | ); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /lib/widgets/loading_dialog.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class LoadingDialog extends StatelessWidget { 4 | const LoadingDialog({Key key}) : super(key: key); 5 | 6 | @override 7 | Widget build(BuildContext context) { 8 | return new WillPopScope( 9 | onWillPop: () async => false, 10 | child: SimpleDialog(key: key, backgroundColor: Colors.white, children: [ 11 | Center( 12 | child: CircularProgressIndicator(), 13 | ) 14 | ])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /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/utils/extension/date_extension.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: your name 3 | * @Date: 2020-12-08 20:57:12 4 | * @LastEditTime: 2020-12-12 12:49:45 5 | * @LastEditors: your name 6 | * @Description: In User Settings Edit 7 | * @FilePath: /todo/lib/utils/extension/date_extension.dart 8 | */ 9 | import 'package:date_format/date_format.dart'; 10 | 11 | extension DateExtension on DateTime { 12 | String format() { 13 | return formatDate(this, [ 14 | yyyy, 15 | '-', 16 | mm, 17 | '-', 18 | dd, 19 | ]); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /lib/modules/task/task/task_binding.dart: -------------------------------------------------------------------------------- 1 | import 'package:get/get.dart'; 2 | import 'package:todo/data/api/task_api.dart'; 3 | import 'package:todo/data/repositories/task_repository.dart'; 4 | 5 | import 'task_controller.dart'; 6 | 7 | class TaskBinding implements Bindings { 8 | @override 9 | void dependencies() { 10 | Get.lazyPut(() => TaskApi()); 11 | Get.lazyPut(() => TaskRepository()); 12 | Get.lazyPut(() => TaskController()); 13 | } 14 | } 15 | 16 | class Persion { 17 | String name; 18 | Persion({ 19 | this.name, 20 | }); 21 | } 22 | -------------------------------------------------------------------------------- /lib/data/providers/task_dao_service.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: your name 3 | * @Date: 2020-12-11 00:05:19 4 | * @LastEditTime: 2020-12-11 00:07:11 5 | * @LastEditors: Please set LastEditors 6 | * @Description: In User Settings Edit 7 | * @FilePath: /todo/lib/data/providers/task_dao_service.dart 8 | */ 9 | 10 | import 'package:get/get.dart'; 11 | import 'package:todo/data/db/task_dao.dart'; 12 | import 'package:todo/data/db/task_database.dart'; 13 | 14 | class TaskDaoController extends GetxService { 15 | TaskDao init() { 16 | TaskDatabase database = TaskDatabase(); 17 | return TaskDao(database); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /lib/data/remote/interceptor/error_interceptor.dart: -------------------------------------------------------------------------------- 1 | import 'package:dio/dio.dart'; 2 | 3 | class ErrorInterceptors extends InterceptorsWrapper { 4 | // final Dio _dio; 5 | 6 | // ErrorInterceptors(this._dio); 7 | 8 | @override 9 | onRequest(RequestOptions options) async { 10 | //没有网络 11 | // var connectivityResult = await (new Connectivity().checkConnectivity()); 12 | // if (connectivityResult == ConnectivityResult.none) { 13 | // return _dio 14 | // .resolve(new ResultData(Code.errorHandleFunction(Code.NETWORK_ERROR, "", false), false, Code.NETWORK_ERROR)); 15 | // } 16 | return options; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /lib/modules/splash/splash_controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:get/get.dart'; 2 | import 'package:todo/data/providers/login_provider.dart'; 3 | import 'package:todo/routes/app_pages.dart'; 4 | 5 | class SplashController extends GetxController { 6 | @override 7 | void onReady() async { 8 | super.onReady(); 9 | 10 | await Future.delayed(Duration(seconds: 3)); 11 | LoginProvider loginProvider = Get.find(); 12 | print(loginProvider); 13 | // 如果未登录就登录 14 | // 如果已登录就去task页面 15 | if (loginProvider.isLogin()) { 16 | Get.offNamed(Routes.TASK); 17 | } else { 18 | Get.offNamed(Routes.LOGIN); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /lib/data/providers/dio_config_service.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:get/get.dart'; 4 | import 'package:path/path.dart'; 5 | import 'package:path_provider/path_provider.dart'; 6 | import 'package:todo/data/remote/dio_config.dart'; 7 | 8 | class DioConfigController extends GetxService { 9 | Future init() async { 10 | Directory appDocDir = await getApplicationDocumentsDirectory(); 11 | String cookiesPath = join(appDocDir.path, ".cookies/"); 12 | DioConfig dioConfig = DioConfig(); 13 | dioConfig.init('https://www.wanandroid.com', proxy: '192.168.62.10:8888', cookiesPath: cookiesPath); 14 | return dioConfig; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /ios/.gitignore: -------------------------------------------------------------------------------- 1 | *.mode1v3 2 | *.mode2v3 3 | *.moved-aside 4 | *.pbxuser 5 | *.perspectivev3 6 | **/*sync/ 7 | .sconsign.dblite 8 | .tags* 9 | **/.vagrant/ 10 | **/DerivedData/ 11 | Icon? 12 | **/Pods/ 13 | **/.symlinks/ 14 | profile 15 | xcuserdata 16 | **/.generated/ 17 | Flutter/App.framework 18 | Flutter/Flutter.framework 19 | Flutter/Flutter.podspec 20 | Flutter/Generated.xcconfig 21 | Flutter/app.flx 22 | Flutter/app.zip 23 | Flutter/flutter_assets/ 24 | Flutter/flutter_export_environment.sh 25 | ServiceDefinitions.json 26 | Runner/GeneratedPluginRegistrant.* 27 | 28 | # Exceptions to above rules. 29 | !default.mode1v3 30 | !default.mode2v3 31 | !default.pbxuser 32 | !default.perspectivev3 33 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.3.50' 3 | repositories { 4 | google() 5 | jcenter() 6 | } 7 | 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:3.5.0' 10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 11 | } 12 | } 13 | 14 | allprojects { 15 | repositories { 16 | google() 17 | jcenter() 18 | } 19 | } 20 | 21 | rootProject.buildDir = '../build' 22 | subprojects { 23 | project.buildDir = "${rootProject.buildDir}/${project.name}" 24 | } 25 | subprojects { 26 | project.evaluationDependsOn(':app') 27 | } 28 | 29 | task clean(type: Delete) { 30 | delete rootProject.buildDir 31 | } 32 | -------------------------------------------------------------------------------- /lib/data/remote/dio_config.dart: -------------------------------------------------------------------------------- 1 | import 'package:dio/dio.dart'; 2 | import 'package:get/get.dart'; 3 | 4 | class DioConfig { 5 | String _baseUrl; 6 | String get baseUrl => _baseUrl; 7 | 8 | String _proxy; 9 | String get proxy => _proxy; 10 | 11 | String _cookiesPath; 12 | String get cookiesPath => _cookiesPath; 13 | 14 | List _interceptors; 15 | 16 | List get interceptors => _interceptors; 17 | 18 | init(String baseUrl, {List interceptors, String proxy, String cookiesPath}) { 19 | _baseUrl = baseUrl; 20 | _interceptors = interceptors; 21 | _proxy = proxy; 22 | _cookiesPath = cookiesPath; 23 | } 24 | 25 | static DioConfig of() => Get.find(); 26 | } 27 | -------------------------------------------------------------------------------- /lib/r.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: your name 3 | * @Date: 2020-12-08 20:56:00 4 | * @LastEditTime: 2020-12-12 15:49:12 5 | * @LastEditors: Please set LastEditors 6 | * @Description: In User Settings Edit 7 | * @FilePath: /todo/lib/r.dart 8 | */ 9 | class R { 10 | static final String assetsImagesProfile = 'assets/images/3.0x/profile.jpg'; 11 | static final String assetsImagesSplash = 'assets/images/3.0x/splash.png'; 12 | static final String assetsImagesGetx1 = 'assets/images/3.0x/getx1.png'; 13 | static final String assetsImagesGetx2 = 'assets/images/3.0x/getx2.png'; 14 | static final String assetsImagesGetx3 = 'assets/images/3.0x/getx3.png'; 15 | static final String assetsImagesGetx4 = 'assets/images/3.0x/getx4.png'; 16 | } 17 | -------------------------------------------------------------------------------- /lib/modules/login/components/text_field_container.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:get/get.dart'; 3 | import 'package:todo/theme/app_theme.dart'; 4 | 5 | class TextFieldContainer extends StatelessWidget { 6 | final Widget child; 7 | TextFieldContainer({ 8 | Key key, 9 | this.child, 10 | }) : super(key: key); 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | return Container( 15 | margin: EdgeInsets.symmetric(vertical: 10), 16 | padding: EdgeInsets.symmetric(horizontal: 20, vertical: 5), 17 | width: context.width * 0.8, 18 | decoration: BoxDecoration( 19 | color: kPrimaryLightColor, 20 | borderRadius: BorderRadius.circular(20), 21 | ), 22 | child: child, 23 | ); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /lib/routes/app_routes.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: your name 3 | * @Date: 2020-12-09 20:10:32 4 | * @LastEditTime: 2020-12-12 14:37:10 5 | * @LastEditors: Please set LastEditors 6 | * @Description: In User Settings Edit 7 | * @FilePath: /todo/lib/routes/app_routes.dart 8 | */ 9 | part of './app_pages.dart'; 10 | 11 | abstract class Routes { 12 | static const INITIAL = '/'; 13 | static const SPLASH = '/splash'; 14 | static const LOGIN = '/login'; 15 | static const HOME = '/home'; 16 | static const PROFILE = '/profile'; 17 | static const SIGN_UP = '/sign_up'; 18 | static const TASK = '/task'; 19 | static const TASK_ADD = '/task-add'; 20 | static const TASK_EDIT = '/task-edit'; 21 | static const TASK_MOTHLY = '/task-mothly'; 22 | static const TASK_DETAILS = '/task-details'; 23 | } 24 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /lib/modules/task/monthly/monthly_binding.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: your name 3 | * @Date: 2020-12-09 20:10:32 4 | * @LastEditTime: 2020-12-09 23:14:37 5 | * @LastEditors: your name 6 | * @Description: In User Settings Edit 7 | * @FilePath: /todo/lib/modules/task/monthly/monthly_binding.dart 8 | */ 9 | import 'package:get/get.dart'; 10 | import 'package:todo/data/api/task_api.dart'; 11 | import 'package:todo/data/repositories/task_repository.dart'; 12 | import 'package:todo/modules/task/monthly/monthly_controller.dart'; 13 | 14 | class MonthlyBinding implements Bindings { 15 | @override 16 | void dependencies() { 17 | Get.lazyPut(() => TaskApi()); 18 | Get.lazyPut(() => TaskRepository()); 19 | Get.lazyPut(() => MonthlyController()); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /lib/data/repositories/login_repository.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: your name 3 | * @Date: 2020-12-08 20:57:12 4 | * @LastEditTime: 2020-12-13 01:22:52 5 | * @LastEditors: Please set LastEditors 6 | * @Description: In User Settings Edit 7 | * @FilePath: /todo/lib/data/repositories/login_repository.dart 8 | */ 9 | import 'package:get/get.dart'; 10 | import 'package:todo/data/api/login_api.dart'; 11 | import 'package:todo/data/model/login_bean.dart'; 12 | 13 | class LoginRepository { 14 | final LoginApi api = Get.find(); 15 | 16 | Future login(String username, String password) { 17 | return api.login(username, password); 18 | } 19 | 20 | Future register( 21 | String username, String password, String repassword) { 22 | return api.register(username, password, repassword); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /lib/modules/login/components/or_divider.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:todo/theme/app_theme.dart'; 3 | 4 | class OrDivider extends StatelessWidget { 5 | const OrDivider({Key key}) : super(key: key); 6 | 7 | @override 8 | Widget build(BuildContext context) { 9 | return Container( 10 | child: Row( 11 | children: [ 12 | _buildDivider(), 13 | Padding( 14 | padding: const EdgeInsets.symmetric(horizontal: 10), 15 | child: Text("OR", style: TextStyle(color: kPrimaryColor, fontWeight: FontWeight.w600)), 16 | ), 17 | _buildDivider(), 18 | ], 19 | ), 20 | ); 21 | } 22 | 23 | Widget _buildDivider() => Expanded( 24 | child: Divider( 25 | color: Color(0xFFD9D9D9), 26 | height: 1.5, 27 | ), 28 | ); 29 | } 30 | -------------------------------------------------------------------------------- /lib/modules/login/components/rounded_input_field.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:todo/modules/login/components/text_field_container.dart'; 3 | import 'package:todo/theme/app_theme.dart'; 4 | 5 | class RoundedInputField extends StatelessWidget { 6 | final String hintText; 7 | final IconData icon; 8 | final ValueChanged onChanged; 9 | const RoundedInputField({ 10 | Key key, 11 | this.hintText, 12 | this.icon, 13 | this.onChanged, 14 | }) : super(key: key); 15 | 16 | @override 17 | Widget build(BuildContext context) { 18 | return TextFieldContainer( 19 | child: TextField( 20 | onChanged: onChanged, 21 | decoration: InputDecoration( 22 | icon: Icon(icon, color: kPrimaryColor), 23 | hintText: hintText, 24 | border: InputBorder.none, 25 | ), 26 | )); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /lib/data/local/local_login_model_reposity.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:get/get.dart'; 5 | import 'package:shared_preferences/shared_preferences.dart'; 6 | import 'package:todo/data/model/login_bean.dart'; 7 | 8 | class LocalLoginModelRepository { 9 | static final String keyLoginModel = 'key_login_model'; 10 | 11 | static saveLoginModel(LoginBean bean) { 12 | SharedPreferences sp = Get.find(); 13 | sp.setString(keyLoginModel, jsonEncode(bean.toJson())); 14 | } 15 | 16 | static LoginBean getLoginModel() { 17 | SharedPreferences sp = Get.find(); 18 | try { 19 | var json = sp.getString(keyLoginModel); 20 | return LoginBean.fromJson(jsonDecode(json)); 21 | } catch (e) { 22 | debugPrint(e.toString()); 23 | return null; 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /lib/modules/login/components/round_paaword_field.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:todo/modules/login/components/text_field_container.dart'; 3 | import 'package:todo/theme/app_theme.dart'; 4 | 5 | class RoundedPasswordField extends StatelessWidget { 6 | final ValueChanged onChanged; 7 | const RoundedPasswordField({ 8 | Key key, 9 | this.onChanged, 10 | }) : super(key: key); 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | return TextFieldContainer( 15 | child: TextField( 16 | onChanged: onChanged, 17 | obscureText: true, 18 | decoration: InputDecoration( 19 | hintText: 'Password', 20 | icon: Icon(Icons.lock, color: kPrimaryColor), 21 | suffixIcon: Icon(Icons.visibility, color: kPrimaryColor), 22 | border: InputBorder.none, 23 | ), 24 | ), 25 | ); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 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/data/remote/interceptor/response_interceptor.dart: -------------------------------------------------------------------------------- 1 | import 'package:dio/dio.dart'; 2 | 3 | class ResponseInterceptors extends InterceptorsWrapper { 4 | @override 5 | onResponse(Response response) async { 6 | // var value; 7 | // try { 8 | // if (response.statusCode >= 200 && 9 | // response.statusCode < 300 && 10 | // response.data != null && 11 | // response.data['errorCode'] == 0) { 12 | // value = AppResponse.success(response.data['data']); 13 | // } 14 | // if (response.data != null && response.data['errorCode'] != 0) { 15 | // value = AppResponse.failure(errorMsg: response.data['errorMsg']?.toString() ?? response.statusMessage); 16 | // } 17 | // } catch (e) { 18 | // print('ResponseInterceptors==catch=' + e.toString()); 19 | // value = new AppResponse.failure(errorMsg: e.toString()); 20 | // } 21 | return response; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /lib/modules/login/components/rounded_button.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:get/get.dart'; 3 | import 'package:todo/theme/app_theme.dart'; 4 | 5 | class RoundedButton extends StatelessWidget { 6 | final VoidCallback onPressed; 7 | final String text; 8 | final Color color, textColor; 9 | 10 | const RoundedButton({Key key, this.onPressed, this.text, this.color = kPrimaryColor, this.textColor = Colors.white}) 11 | : super(key: key); 12 | 13 | @override 14 | Widget build(BuildContext context) { 15 | return Container( 16 | width: Get.width * 0.8, 17 | child: ClipRRect( 18 | borderRadius: BorderRadius.circular(29), 19 | child: FlatButton( 20 | color: kPrimaryColor, 21 | padding: EdgeInsets.symmetric(vertical: 20, horizontal: 40), 22 | onPressed: onPressed, 23 | child: Text( 24 | text, 25 | style: TextStyle(color: textColor), 26 | ), 27 | ), 28 | ), 29 | ); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /lib/utils/dependency_injection.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: your name 3 | * @Date: 2020-12-08 20:57:12 4 | * @LastEditTime: 2020-12-11 01:38:08 5 | * @LastEditors: Please set LastEditors 6 | * @Description: In User Settings Edit 7 | * @FilePath: /todo/lib/utils/dependency_injection.dart 8 | */ 9 | import 'package:get/get.dart'; 10 | import 'package:todo/data/providers/app_sp_service.dart'; 11 | import 'package:todo/data/providers/dio_config_service.dart'; 12 | import 'package:todo/data/providers/login_provider.dart'; 13 | import 'package:todo/data/providers/task_dao_service.dart'; 14 | import 'package:todo/data/remote/dio_client.dart'; 15 | 16 | /// 依赖注入 17 | class DenpendencyInjection { 18 | static Future init() async { 19 | // shared_preferences 20 | await Get.putAsync(() => AppSpController().init()); 21 | // dio配置信息 22 | await Get.putAsync(() => DioConfigController().init()); 23 | // 网络请求 24 | Get.put(DioClient()); 25 | // 登录信息提供者 26 | Get.put(LoginProvider()); 27 | // 数据库 28 | Get.put(TaskDaoController().init()); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /lib/theme/app_theme.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: your name 3 | * @Date: 2020-12-08 20:57:12 4 | * @LastEditTime: 2020-12-09 22:30:22 5 | * @LastEditors: Please set LastEditors 6 | * @Description: In User Settings Edit 7 | * @FilePath: /todo/lib/theme/app_theme.dart 8 | */ 9 | import 'package:flutter/material.dart'; 10 | import 'package:google_fonts/google_fonts.dart'; 11 | 12 | const kPrimaryColor = Color(0xFFc44dff); 13 | const kPrimaryLightColor = Color(0xFFf0d3ff); 14 | const primaryDarkColor = Color(0xFF8936b3); 15 | const secondaryColor = Color(0xFFe1a6ff); 16 | const secondaryLightColor = Color(0xFFe1a6ff); 17 | const secondaryDarkColor = Color(0xFFe1a6ff); 18 | // const primaryTextColor = Color(0xFFF1E6FF); 19 | // const secondaryTextColor = Color(0xFFF1E6FF); 20 | 21 | ThemeData get appThemeData => ThemeData( 22 | primaryColor: kPrimaryColor, 23 | primaryColorLight: kPrimaryLightColor, 24 | scaffoldBackgroundColor: Colors.white, 25 | accentColor: kPrimaryColor, 26 | appBarTheme: appBarTheme, 27 | textTheme: GoogleFonts.poppinsTextTheme()); 28 | 29 | AppBarTheme get appBarTheme => AppBarTheme(); 30 | -------------------------------------------------------------------------------- /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:todo/modules/splash/splash_page.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(SplashPage()); 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 | -------------------------------------------------------------------------------- /lib/modules/login/components/alread_have_an_account_check.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: your name 3 | * @Date: 2020-12-08 20:57:12 4 | * @LastEditTime: 2020-12-11 00:13:41 5 | * @LastEditors: your name 6 | * @Description: In User Settings Edit 7 | * @FilePath: /todo/lib/modules/login/components/alread_have_an_account_check.dart 8 | */ 9 | import 'package:flutter/material.dart'; 10 | import 'package:todo/theme/app_theme.dart'; 11 | 12 | class AlreadHaveAnAccoutCheck extends StatelessWidget { 13 | final bool login; 14 | final GestureTapCallback onTap; 15 | const AlreadHaveAnAccoutCheck({ 16 | Key key, 17 | this.login, 18 | this.onTap, 19 | }) : super(key: key); 20 | 21 | @override 22 | Widget build(BuildContext context) { 23 | return Row( 24 | mainAxisAlignment: MainAxisAlignment.center, 25 | children: [ 26 | Text( 27 | login ? "Don't have a Accont?" : "Already have a Accont ? ", 28 | style: TextStyle(color: kPrimaryColor), 29 | ), 30 | GestureDetector( 31 | onTap: onTap, 32 | child: Text( 33 | login ? 'Sign up' : "Sign in", 34 | style: TextStyle(color: kPrimaryColor, fontWeight: FontWeight.bold), 35 | ), 36 | ) 37 | ], 38 | ); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /ios/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Flutter (1.0.0) 3 | - FMDB (2.7.5): 4 | - FMDB/standard (= 2.7.5) 5 | - FMDB/standard (2.7.5) 6 | - path_provider (0.0.1): 7 | - Flutter 8 | - shared_preferences (0.0.1): 9 | - Flutter 10 | - sqflite (0.0.2): 11 | - Flutter 12 | - FMDB (>= 2.7.5) 13 | 14 | DEPENDENCIES: 15 | - Flutter (from `Flutter`) 16 | - path_provider (from `.symlinks/plugins/path_provider/ios`) 17 | - shared_preferences (from `.symlinks/plugins/shared_preferences/ios`) 18 | - sqflite (from `.symlinks/plugins/sqflite/ios`) 19 | 20 | SPEC REPOS: 21 | trunk: 22 | - FMDB 23 | 24 | EXTERNAL SOURCES: 25 | Flutter: 26 | :path: Flutter 27 | path_provider: 28 | :path: ".symlinks/plugins/path_provider/ios" 29 | shared_preferences: 30 | :path: ".symlinks/plugins/shared_preferences/ios" 31 | sqflite: 32 | :path: ".symlinks/plugins/sqflite/ios" 33 | 34 | SPEC CHECKSUMS: 35 | Flutter: 0e3d915762c693b495b44d77113d4970485de6ec 36 | FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a 37 | path_provider: abfe2b5c733d04e238b0d8691db0cfd63a27a93c 38 | shared_preferences: af6bfa751691cdc24be3045c43ec037377ada40d 39 | sqflite: 6d358c025f5b867b29ed92fc697fd34924e11904 40 | 41 | PODFILE CHECKSUM: aafe91acc616949ddb318b77800a7f51bffa2a4c 42 | 43 | COCOAPODS: 1.9.1 44 | -------------------------------------------------------------------------------- /lib/modules/splash/splash_page.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: your name 3 | * @Date: 2020-12-08 20:56:00 4 | * @LastEditTime: 2020-12-12 14:38:37 5 | * @LastEditors: your name 6 | * @Description: In User Settings Edit 7 | * @FilePath: /todo/lib/modules/splash/splash_page.dart 8 | */ 9 | import 'package:flutter/material.dart'; 10 | import 'package:get/get_state_manager/get_state_manager.dart'; 11 | import 'package:todo/modules/splash/splash_controller.dart'; 12 | import 'package:todo/r.dart'; 13 | 14 | class SplashPage extends StatelessWidget { 15 | @override 16 | Widget build(BuildContext context) { 17 | return Scaffold( 18 | body: GetBuilder( 19 | builder: (_) { 20 | return Column( 21 | children: [ 22 | SizedBox( 23 | height: 120, 24 | ), 25 | Text('WELCOME TO HERE', 26 | style: Theme.of(context) 27 | .textTheme 28 | .headline5 29 | .copyWith(fontWeight: FontWeight.bold)), 30 | SizedBox( 31 | height: 120, 32 | ), 33 | Image.asset( 34 | R.assetsImagesSplash, 35 | fit: BoxFit.fitWidth, 36 | ), 37 | ], 38 | ); 39 | }, 40 | ), 41 | ); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /lib/data/remote/app_response.dart: -------------------------------------------------------------------------------- 1 | import 'package:dio/dio.dart'; 2 | import 'package:todo/data/remote/app_exceptions.dart'; 3 | 4 | class AppResponse { 5 | bool _ok; 6 | bool get ok => _ok ?? false; 7 | dynamic _data; 8 | dynamic get data => _data; 9 | AppException _error; 10 | AppException get error => _error ?? AppException(); 11 | 12 | factory AppResponse.obtain(Response response) { 13 | try { 14 | if (response.statusCode >= 200 && 15 | response.statusCode < 300 && 16 | response.data != null && 17 | response.data['errorCode'] == 0) { 18 | return AppResponse._success(response.data['data']); 19 | } 20 | if (response.data != null && response.data['errorCode'] != 0) { 21 | return AppResponse._failure(errorMsg: response.data['errorMsg']?.toString() ?? response.statusMessage); 22 | } 23 | return AppResponse._failureFromError(HttpException(response.statusMessage, response.statusCode)); 24 | } catch (e) { 25 | print('AppResponse==obtain=' + e.toString()); 26 | return AppResponse._failure(errorMsg: e.toString()); 27 | } 28 | } 29 | 30 | AppResponse._success([dynamic data]) { 31 | _data = data; 32 | _ok = true; 33 | } 34 | AppResponse._failure({String errorMsg}) { 35 | _error = AppException.obtainFromMessage(errorMsg); 36 | } 37 | AppResponse._failureFromError(AppException error) { 38 | _error = error; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /lib/data/api/login_api.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: your name 3 | * @Date: 2020-12-08 20:57:12 4 | * @LastEditTime: 2020-12-13 01:21:11 5 | * @LastEditors: Please set LastEditors 6 | * @Description: In User Settings Edit 7 | * @FilePath: /todo/lib/data/api/login_api.dart 8 | */ 9 | import 'package:get/get.dart'; 10 | import 'package:todo/data/model/login_bean.dart'; 11 | import 'package:todo/data/remote/app_response.dart'; 12 | import 'package:todo/data/remote/dio_client.dart'; 13 | 14 | class LoginApi { 15 | final String _login = '/user/login'; 16 | final String _register = '/user/register'; 17 | final DioClient _dio = Get.find(); 18 | 19 | Future login(String username, String password) async { 20 | AppResponse appResponse = await _dio.post(_login, queryParameters: { 21 | "username": username, 22 | "password": password, 23 | }); 24 | if (appResponse.ok) { 25 | return LoginBean.fromJson(appResponse.data); 26 | } else { 27 | throw appResponse.error; 28 | } 29 | } 30 | 31 | Future register( 32 | String username, String password, String repassword) async { 33 | AppResponse appResponse = await _dio.post(_register, queryParameters: { 34 | "username": username, 35 | "password": password, 36 | "repassword": password 37 | }); 38 | if (appResponse.ok) { 39 | return LoginBean.fromJson(appResponse.data); 40 | } else { 41 | throw appResponse.error; 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /ios/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | # platform :ios, '9.0' 3 | 4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 6 | 7 | project 'Runner', { 8 | 'Debug' => :debug, 9 | 'Profile' => :release, 10 | 'Release' => :release, 11 | } 12 | 13 | def flutter_root 14 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) 15 | unless File.exist?(generated_xcode_build_settings_path) 16 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" 17 | end 18 | 19 | File.foreach(generated_xcode_build_settings_path) do |line| 20 | matches = line.match(/FLUTTER_ROOT\=(.*)/) 21 | return matches[1].strip if matches 22 | end 23 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" 24 | end 25 | 26 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) 27 | 28 | flutter_ios_podfile_setup 29 | 30 | target 'Runner' do 31 | use_frameworks! 32 | use_modular_headers! 33 | 34 | flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) 35 | end 36 | 37 | post_install do |installer| 38 | installer.pods_project.targets.each do |target| 39 | flutter_additional_ios_build_settings(target) 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /lib/main.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: your name 3 | * @Date: 2020-12-08 20:56:00 4 | * @LastEditTime: 2020-12-11 23:06:07 5 | * @LastEditors: Please set LastEditors 6 | * @Description: In User Settings Edit 7 | * @FilePath: /todo/lib/main.dart 8 | */ 9 | import 'package:flutter/material.dart'; 10 | import 'package:get/get.dart'; 11 | import 'package:todo/modules/splash/splash_binding.dart'; 12 | import 'package:todo/modules/splash/splash_page.dart'; 13 | import 'package:todo/routes/app_pages.dart'; 14 | import 'package:todo/theme/app_theme.dart'; 15 | 16 | import 'utils/dependency_injection.dart'; 17 | import 'utils/gloab_config.dart'; 18 | 19 | void main() async { 20 | WidgetsFlutterBinding.ensureInitialized(); 21 | await GloabConfig.init(); 22 | await DenpendencyInjection.init(); 23 | runApp(GetMaterialApp( 24 | debugShowCheckedModeBanner: false, 25 | initialRoute: '/', 26 | builder: (context, child) => Scaffold( 27 | // Global GestureDetector that will dismiss the keyboard 28 | body: GestureDetector( 29 | onTap: () { 30 | hideKeyboard(context); 31 | }, 32 | child: child, 33 | ), 34 | ), 35 | theme: appThemeData, 36 | defaultTransition: Transition.fade, 37 | getPages: AppPages.pages, 38 | initialBinding: SplashBinding(), 39 | home: SplashPage(), 40 | )); 41 | } 42 | 43 | void hideKeyboard(BuildContext context) { 44 | FocusScopeNode currentFocus = FocusScope.of(context); 45 | if (!currentFocus.hasPrimaryFocus && currentFocus.focusedChild != null) { 46 | FocusManager.instance.primaryFocus.unfocus(); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /lib/modules/task/task/task_page.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: your name 3 | * @Date: 2020-12-09 20:10:32 4 | * @LastEditTime: 2020-12-13 01:38:49 5 | * @LastEditors: Please set LastEditors 6 | * @Description: In User Settings Edit 7 | * @FilePath: /todo/lib/modules/task/task/task_page.dart 8 | */ 9 | import 'package:flutter/material.dart'; 10 | import 'package:get/get.dart'; 11 | import 'package:todo/modules/task/task/task_controller.dart'; 12 | import 'package:todo/routes/app_pages.dart'; 13 | import 'components/body.dart'; 14 | 15 | class TaskPage extends GetView { 16 | @override 17 | Widget build(BuildContext context) { 18 | return Scaffold( 19 | appBar: AppBar(title: Text('My Task')), 20 | body: Body(), 21 | floatingActionButton: FloatingActionButton( 22 | onPressed: () { 23 | Get.toNamed(Routes.TASK_ADD); 24 | }, 25 | child: Icon(Icons.add), 26 | ), 27 | floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked, 28 | bottomNavigationBar: BottomAppBar( 29 | shape: CircularNotchedRectangle(), 30 | child: Row( 31 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 32 | children: [ 33 | IconButton( 34 | icon: Icon(Icons.calendar_today_sharp), 35 | onPressed: () { 36 | Get.toNamed(Routes.TASK_MOTHLY); 37 | }, 38 | ), 39 | IconButton( 40 | icon: Icon(Icons.settings), 41 | onPressed: () { 42 | Get.toNamed(Routes.PROFILE); 43 | }, 44 | ), 45 | ], 46 | ), 47 | ), 48 | ); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /lib/modules/login/login_controller.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: your name 3 | * @Date: 2020-12-08 20:57:12 4 | * @LastEditTime: 2020-12-13 01:24:32 5 | * @LastEditors: Please set LastEditors 6 | * @Description: In User Settings Edit 7 | * @FilePath: /todo/lib/modules/login/login_controller.dart 8 | */ 9 | import 'package:get/get.dart'; 10 | import 'package:todo/data/model/login_bean.dart'; 11 | import 'package:todo/data/local/local_login_model_reposity.dart'; 12 | import 'package:todo/data/repositories/login_repository.dart'; 13 | import 'package:todo/routes/app_pages.dart'; 14 | import 'package:todo/utils/extension/get_extension.dart'; 15 | 16 | class LoginController extends GetxController { 17 | final LoginRepository repository = Get.find(); 18 | String _username; 19 | String _password; 20 | 21 | void onUsernameChanged(String username) { 22 | _username = username.trim(); 23 | } 24 | 25 | void onPasswordChanged(String password) { 26 | _password = password.trim(); 27 | } 28 | 29 | sumit() async { 30 | if (_username == null || _username.trim.toString().isEmpty) { 31 | Get.snackbar('Hi', 'Your Email not be null'); 32 | return; 33 | } 34 | 35 | if (_password == null || _password.trim.toString().isEmpty) { 36 | Get.snackbar('Hi', 'Your Password not be null'); 37 | return; 38 | } 39 | Get.loading(); 40 | try { 41 | LoginBean bean = await repository.login(_username, _password); 42 | Get.dismiss(); 43 | LocalLoginModelRepository.saveLoginModel(bean); 44 | Get.offAllNamed(Routes.TASK); 45 | } catch (e) { 46 | Get.dismiss(); 47 | Get.snackbar('Error', e.message ?? "登录失败"); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | todo 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | $(FLUTTER_BUILD_NAME) 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UISupportedInterfaceOrientations 30 | 31 | UIInterfaceOrientationPortrait 32 | UIInterfaceOrientationLandscapeLeft 33 | UIInterfaceOrientationLandscapeRight 34 | 35 | UISupportedInterfaceOrientations~ipad 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationPortraitUpsideDown 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | UIViewControllerBasedStatusBarAppearance 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /lib/modules/task/monthly/monthly_controller.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: your name 3 | * @Date: 2020-12-09 20:10:32 4 | * @LastEditTime: 2020-12-12 13:12:15 5 | * @LastEditors: Please set LastEditors 6 | * @Description: In User Settings Edit 7 | * @FilePath: /todo/lib/modules/task/monthly/monthly_controller.dart 8 | */ 9 | import 'package:get/get.dart'; 10 | import 'package:table_calendar/table_calendar.dart'; 11 | import 'package:todo/data/db/task_dao.dart'; 12 | import 'package:todo/data/db/task_database.dart'; 13 | import 'package:todo/modules/task/task/task_controller.dart'; 14 | import 'package:todo/utils/extension/get_extension.dart'; 15 | import 'package:todo/utils/extension/date_extension.dart'; 16 | 17 | class MonthlyController extends GetxController { 18 | final CalendarController calendarController = CalendarController(); 19 | final TaskDao taskDao = Get.find(); 20 | 21 | @override 22 | void onReady() { 23 | super.onReady(); 24 | getTasks(); 25 | } 26 | 27 | final List tasks = []; 28 | DateTime _date = DateTime.now(); 29 | getTasks() async { 30 | Get.loading(); 31 | try { 32 | var list = await taskDao.getTasksWithDateStr(_date.format()); 33 | if (list != null && list.isNotEmpty) { 34 | tasks.addAll(list); 35 | } 36 | } catch (e) {} 37 | update(); 38 | Get.dismiss(); 39 | } 40 | 41 | selectedDate(DateTime date) async { 42 | tasks.clear(); 43 | _date = date; 44 | await getTasks(); 45 | } 46 | 47 | modifyTaskStatus(Task task) async { 48 | try { 49 | TaskController taskController = Get.find(); 50 | await taskController.modifyTaskStatus(task); 51 | } catch (e) {} 52 | update(); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /lib/data/remote/interceptor/token_interceptor.dart: -------------------------------------------------------------------------------- 1 | import 'package:dio/dio.dart'; 2 | 3 | class TokenInterceptors extends InterceptorsWrapper { 4 | String _token; 5 | 6 | @override 7 | onRequest(RequestOptions options) async { 8 | //授权码 9 | if (_token == null) { 10 | var authorizationCode = await getAuthorization(); 11 | if (authorizationCode != null) { 12 | _token = authorizationCode; 13 | } 14 | } 15 | options.headers["Authorization"] = _token; 16 | return options; 17 | } 18 | 19 | @override 20 | onResponse(Response response) async { 21 | // try { 22 | // var responseJson = response.data; 23 | // if (response.statusCode == 201 && responseJson["token"] != null) { 24 | // _token = responseJson["token"]; 25 | // await LocalStorage.save(Config.MEMBER_TOKEN, _token); 26 | // } 27 | // } catch (e) { 28 | // Logger.w("token onResponse error", e: e); 29 | // } 30 | return response; 31 | } 32 | 33 | ///清除授权 34 | clearAuthorization() { 35 | // this._token = null; 36 | // LocalStorage.remove(Config.MEMBER_TOKEN); 37 | } 38 | 39 | ///获取授权token 40 | getAuthorization() async { 41 | // String token = await LocalStorage.get(Config.MEMBER_TOKEN); 42 | // if (token == null) { 43 | // String memberId = await LocalStorage.get(Config.MEMBER_ID); 44 | // if (memberId == null) { 45 | // //!!!提示输入账号密码 46 | // } else { 47 | // var bytes = utf8.encode(memberId); 48 | // var basic = base64.encode(bytes); 49 | // //通过 basic 去获取token,获取到设置,返回token 50 | // return "Basic $basic"; 51 | // } 52 | // } else { 53 | // this._token = token; 54 | // return token; 55 | // } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /lib/widgets/start_card.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: your name 3 | * @Date: 2020-12-12 15:28:39 4 | * @LastEditTime: 2020-12-12 15:31:00 5 | * @LastEditors: Please set LastEditors 6 | * @Description: In User Settings Edit 7 | * @FilePath: /todo/lib/widgets/start_card.dart 8 | */ 9 | import 'package:flutter/material.dart'; 10 | 11 | class TaskCardWidget extends StatelessWidget { 12 | final String title; 13 | final String desc; 14 | 15 | TaskCardWidget({this.title, this.desc}); 16 | 17 | @override 18 | Widget build(BuildContext context) { 19 | return Container( 20 | width: double.infinity, 21 | padding: EdgeInsets.symmetric( 22 | vertical: 32.0, 23 | horizontal: 24.0, 24 | ), 25 | margin: EdgeInsets.only( 26 | bottom: 20.0, 27 | ), 28 | decoration: BoxDecoration( 29 | color: Colors.white, 30 | borderRadius: BorderRadius.circular(20.0), 31 | ), 32 | child: Column( 33 | crossAxisAlignment: CrossAxisAlignment.start, 34 | children: [ 35 | Text( 36 | 'Get Started!', 37 | style: TextStyle( 38 | color: Color(0xFF211551), 39 | fontSize: 22.0, 40 | fontWeight: FontWeight.bold, 41 | ), 42 | ), 43 | Padding( 44 | padding: EdgeInsets.only( 45 | top: 10.0, 46 | ), 47 | child: Text( 48 | "Hello User! Welcome to TODO app, this is a default task that you can edit or delete to start using the app", 49 | style: TextStyle( 50 | fontSize: 16.0, 51 | color: Color(0xFF86829D), 52 | height: 1.5, 53 | ), 54 | ), 55 | ) 56 | ], 57 | ), 58 | ); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /lib/data/model/task_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:todo/data/db/task_database.dart'; 2 | 3 | class TaskModel { 4 | /* 5 | { 6 | "curPage": 1, 7 | "datas": [ 8 | { 9 | "completeDate": null, 10 | "completeDateStr": "", 11 | "content": "", 12 | "date": 1607270400000, 13 | "dateStr": "2020-12-07", 14 | "id": 24902, 15 | "priority": 0, 16 | "status": 0, 17 | "title": "SSSS", 18 | "type": 0, 19 | "userId": 82504 20 | } 21 | ], 22 | "offset": 0, 23 | "over": true, 24 | "pageCount": 1, 25 | "size": 20, 26 | "total": 4 27 | } 28 | */ 29 | 30 | int curPage; 31 | List datas; 32 | int offset; 33 | bool over; 34 | int pageCount; 35 | int size; 36 | int total; 37 | 38 | TaskModel({ 39 | this.curPage, 40 | this.datas, 41 | this.offset, 42 | this.over, 43 | this.pageCount, 44 | this.size, 45 | this.total, 46 | }); 47 | TaskModel.fromJson(Map json) { 48 | curPage = json["curPage"]?.toInt(); 49 | if (json["datas"] != null) { 50 | var v = json["datas"]; 51 | var arr0 = List(); 52 | v.forEach((v) { 53 | arr0.add(Task.fromJson(v)); 54 | }); 55 | datas = arr0; 56 | } 57 | offset = json["offset"]?.toInt(); 58 | over = json["over"]; 59 | pageCount = json["pageCount"]?.toInt(); 60 | size = json["size"]?.toInt(); 61 | total = json["total"]?.toInt(); 62 | } 63 | Map toJson() { 64 | final Map data = Map(); 65 | data["curPage"] = curPage; 66 | if (datas != null) { 67 | var v = datas; 68 | var arr0 = List(); 69 | v.forEach((v) { 70 | arr0.add(v.toJson()); 71 | }); 72 | data["datas"] = arr0; 73 | } 74 | data["offset"] = offset; 75 | data["over"] = over; 76 | data["pageCount"] = pageCount; 77 | data["size"] = size; 78 | data["total"] = total; 79 | return data; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /android/app/build.gradle: -------------------------------------------------------------------------------- 1 | def localProperties = new Properties() 2 | def localPropertiesFile = rootProject.file('local.properties') 3 | if (localPropertiesFile.exists()) { 4 | localPropertiesFile.withReader('UTF-8') { reader -> 5 | localProperties.load(reader) 6 | } 7 | } 8 | 9 | def flutterRoot = localProperties.getProperty('flutter.sdk') 10 | if (flutterRoot == null) { 11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") 12 | } 13 | 14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 15 | if (flutterVersionCode == null) { 16 | flutterVersionCode = '1' 17 | } 18 | 19 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 20 | if (flutterVersionName == null) { 21 | flutterVersionName = '1.0' 22 | } 23 | 24 | apply plugin: 'com.android.application' 25 | apply plugin: 'kotlin-android' 26 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 27 | 28 | android { 29 | compileSdkVersion 29 30 | 31 | sourceSets { 32 | main.java.srcDirs += 'src/main/kotlin' 33 | } 34 | 35 | lintOptions { 36 | disable 'InvalidPackage' 37 | } 38 | 39 | defaultConfig { 40 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 41 | applicationId "com.example.todo" 42 | minSdkVersion 16 43 | targetSdkVersion 29 44 | versionCode flutterVersionCode.toInteger() 45 | versionName flutterVersionName 46 | } 47 | 48 | buildTypes { 49 | release { 50 | // TODO: Add your own signing config for the release build. 51 | // Signing with the debug keys for now, so `flutter run --release` works. 52 | signingConfig signingConfigs.debug 53 | } 54 | } 55 | } 56 | 57 | flutter { 58 | source '../..' 59 | } 60 | 61 | dependencies { 62 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 63 | } 64 | -------------------------------------------------------------------------------- /lib/modules/signup/sign_up_controller.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: your name 3 | * @Date: 2020-12-09 20:10:32 4 | * @LastEditTime: 2020-12-13 01:32:23 5 | * @LastEditors: Please set LastEditors 6 | * @Description: In User Settings Edit 7 | * @FilePath: /todo/lib/modules/signup/sign_up_controller.dart 8 | */ 9 | import 'package:get/get.dart'; 10 | import 'package:todo/data/local/local_login_model_reposity.dart'; 11 | import 'package:todo/data/model/login_bean.dart'; 12 | import 'package:todo/data/repositories/login_repository.dart'; 13 | import 'package:todo/routes/app_pages.dart'; 14 | import 'package:todo/utils/extension/get_extension.dart'; 15 | 16 | class SignUpController extends GetxController { 17 | final LoginRepository repository = Get.find(); 18 | String _username; 19 | String _password; 20 | String _repassword; 21 | 22 | void onUsernameChanged(String username) { 23 | _username = username.trim(); 24 | } 25 | 26 | void onPasswordChanged(String password) { 27 | _password = password.trim(); 28 | } 29 | 30 | void onRePasswordChanged(String repassword) { 31 | _repassword = repassword.trim(); 32 | } 33 | 34 | sumit() async { 35 | if (_username == null || _username.trim.toString().isEmpty) { 36 | Get.snackbar('Hi', 'Your Email not be null'); 37 | return; 38 | } 39 | 40 | if (_password == null || _password.trim.toString().isEmpty) { 41 | Get.snackbar('Hi', 'Your Password not be null'); 42 | return; 43 | } 44 | if (_repassword == null || _repassword.trim.toString().isEmpty) { 45 | Get.snackbar('Hi', 'Your _repassword not be null'); 46 | return; 47 | } 48 | Get.loading(); 49 | try { 50 | LoginBean bean = 51 | await repository.register(_username, _password, _repassword); 52 | Get.dismiss(); 53 | LocalLoginModelRepository.saveLoginModel(bean); 54 | Get.offAllNamed(Routes.TASK); 55 | } catch (e) { 56 | Get.dismiss(); 57 | Get.snackbar('Error', e.message ?? "注册失败"); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /lib/modules/task/task/components/round_check_box.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: your name 3 | * @Date: 2020-12-08 20:57:12 4 | * @LastEditTime: 2020-12-08 22:00:12 5 | * @LastEditors: Please set LastEditors 6 | * @Description: In User Settings Edit 7 | * @FilePath: /todo/lib/modules/task/task/components/round_check_box.dart 8 | */ 9 | import 'package:flutter/material.dart'; 10 | 11 | class RoundCheckBox extends StatefulWidget { 12 | final bool value; 13 | final Size size; 14 | final Color color; 15 | 16 | final ValueChanged onChanged; 17 | 18 | RoundCheckBox( 19 | {Key key, 20 | @required this.value, 21 | this.onChanged, 22 | this.size = const Size(30, 30), 23 | this.color}) 24 | : super(key: key); 25 | 26 | @override 27 | _RoundCheckBoxState createState() => _RoundCheckBoxState(); 28 | } 29 | 30 | class _RoundCheckBoxState extends State { 31 | bool _value; 32 | @override 33 | void initState() { 34 | super.initState(); 35 | _value = widget.value; 36 | } 37 | 38 | _changeState() { 39 | setState(() { 40 | _value = !_value; 41 | widget.onChanged?.call(_value); 42 | }); 43 | } 44 | 45 | @override 46 | Widget build(BuildContext context) { 47 | return InkWell( 48 | onTap: _changeState, 49 | child: Container( 50 | width: widget.size.width, 51 | height: widget.size.height, 52 | decoration: BoxDecoration( 53 | shape: BoxShape.circle, 54 | border: Border.all( 55 | color: widget.color ?? Theme.of(context).accentColor, 56 | width: 4)), 57 | child: _value 58 | ? Padding( 59 | padding: EdgeInsets.all(2), 60 | child: Container( 61 | decoration: BoxDecoration( 62 | shape: BoxShape.circle, 63 | color: widget.color ?? Theme.of(context).accentColor, 64 | ), 65 | )) 66 | : null), 67 | ); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /lib/data/remote/app_dio.dart: -------------------------------------------------------------------------------- 1 | import 'package:cookie_jar/cookie_jar.dart'; 2 | import 'package:dio/adapter.dart'; 3 | import 'package:dio/dio.dart'; 4 | import 'package:dio_cookie_manager/dio_cookie_manager.dart'; 5 | import 'package:dio_http_cache/dio_http_cache.dart'; 6 | import 'package:flutter/foundation.dart'; 7 | 8 | import 'dio_config.dart'; 9 | 10 | const _defaultConnectTimeout = Duration.millisecondsPerMinute; 11 | const _defaultSendTimeout = Duration.millisecondsPerMinute; 12 | const _defaultReceiveTimeout = Duration.millisecondsPerMinute; 13 | 14 | class AppDio with DioMixin implements Dio { 15 | DioConfig _dioConfig = DioConfig.of(); 16 | AppDio._([BaseOptions options]) { 17 | options ??= BaseOptions( 18 | baseUrl: _dioConfig.baseUrl, 19 | contentType: 'application/json', 20 | connectTimeout: _defaultConnectTimeout, 21 | sendTimeout: _defaultSendTimeout, 22 | receiveTimeout: _defaultReceiveTimeout, 23 | ); 24 | 25 | this.options = options; 26 | 27 | //DioCacheManager 28 | interceptors.add(DioCacheManager( 29 | CacheConfig( 30 | baseUrl: _dioConfig.baseUrl, 31 | ), 32 | ).interceptor); 33 | //Cookie管理 34 | interceptors.add(CookieManager(PersistCookieJar(dir: _dioConfig.cookiesPath))); 35 | 36 | if (kDebugMode) { 37 | interceptors.add(LogInterceptor( 38 | responseBody: true, 39 | error: true, 40 | requestHeader: false, 41 | responseHeader: false, 42 | request: false, 43 | requestBody: true)); 44 | } 45 | if (_dioConfig.interceptors?.isNotEmpty ?? false) { 46 | interceptors.addAll(interceptors); 47 | } 48 | httpClientAdapter = DefaultHttpClientAdapter(); 49 | } 50 | 51 | setProxy(String proxy) { 52 | (httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate = (client) { 53 | // config the http client 54 | client.findProxy = (uri) { 55 | //proxy all request to localhost:8888 56 | return "PROXY $proxy"; 57 | }; 58 | // you can also create a HttpClient to dio 59 | // return HttpClient(); 60 | }; 61 | } 62 | 63 | static Dio getInstance() => AppDio._(); 64 | } 65 | -------------------------------------------------------------------------------- /lib/data/model/login_bean.dart: -------------------------------------------------------------------------------- 1 | /// 2 | /// Code generated by jsonToDartModel https://ashamp.github.io/jsonToDartModel/ 3 | /// 4 | class LoginBean { 5 | /* 6 | { 7 | "admin": false, 8 | "chapterTops": [ 9 | null 10 | ], 11 | "coinCount": 0, 12 | "collectIds": [ 13 | null 14 | ], 15 | "email": "", 16 | "icon": "", 17 | "id": 82504, 18 | "nickname": "513960124@qq.com", 19 | "password": "", 20 | "publicName": "513960124@qq.com", 21 | "token": "", 22 | "type": 0, 23 | "username": "513960124@qq.com" 24 | } 25 | */ 26 | 27 | bool admin; 28 | List chapterTops; 29 | int coinCount; 30 | List collectIds; 31 | String email; 32 | String icon; 33 | int id; 34 | String nickname; 35 | String password; 36 | String publicName; 37 | String token; 38 | int type; 39 | String username; 40 | 41 | LoginBean({ 42 | this.admin, 43 | this.chapterTops, 44 | this.coinCount, 45 | this.collectIds, 46 | this.email, 47 | this.icon, 48 | this.id, 49 | this.nickname, 50 | this.password, 51 | this.publicName, 52 | this.token, 53 | this.type, 54 | this.username, 55 | }); 56 | LoginBean.fromJson(Map json) { 57 | admin = json["admin"]; 58 | coinCount = json["coinCount"]?.toInt(); 59 | email = json["email"]?.toString(); 60 | icon = json["icon"]?.toString(); 61 | id = json["id"]?.toInt(); 62 | nickname = json["nickname"]?.toString(); 63 | password = json["password"]?.toString(); 64 | publicName = json["publicName"]?.toString(); 65 | token = json["token"]?.toString(); 66 | type = json["type"]?.toInt(); 67 | username = json["username"]?.toString(); 68 | } 69 | Map toJson() { 70 | final Map data = Map(); 71 | data["admin"] = admin; 72 | data["coinCount"] = coinCount; 73 | data["email"] = email; 74 | data["icon"] = icon; 75 | data["id"] = id; 76 | data["nickname"] = nickname; 77 | data["password"] = password; 78 | data["publicName"] = publicName; 79 | data["token"] = token; 80 | data["type"] = type; 81 | data["username"] = username; 82 | return data; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /lib/data/db/task_database.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: your name 3 | * @Date: 2020-12-08 20:57:12 4 | * @LastEditTime: 2020-12-12 10:48:23 5 | * @LastEditors: Please set LastEditors 6 | * @Description: In User Settings Edit 7 | * @FilePath: /todo/lib/data/db/task_database.dart 8 | */ 9 | import 'dart:io'; 10 | 11 | import 'package:moor/ffi.dart'; 12 | import 'package:moor/moor.dart'; 13 | import 'package:path/path.dart'; 14 | import 'package:path_provider/path_provider.dart'; 15 | import 'package:todo/data/db/task_dao.dart'; 16 | import 'package:todo/utils/extension/date_extension.dart'; 17 | part 'task_database.g.dart'; 18 | 19 | class Tasks extends Table { 20 | // 可空类型 21 | IntColumn get completeDate => integer().nullable()(); 22 | TextColumn get completeDateStr => text().nullable()(); 23 | TextColumn get content => text().nullable()(); 24 | 25 | // 为空自动生成默认值 26 | IntColumn get date => 27 | integer().clientDefault(() => DateTime.now().millisecondsSinceEpoch)(); 28 | 29 | // 为空自动生成默认值 30 | TextColumn get dateStr => 31 | text().nullable().clientDefault(() => DateTime.now().format())(); 32 | 33 | // 主键 34 | IntColumn get id => integer().nullable().autoIncrement()(); 35 | 36 | // 为空自动生成默认值 37 | IntColumn get priority => integer().nullable().withDefault(Constant(0))(); 38 | 39 | // 为空自动生成默认值 40 | IntColumn get status => integer().nullable().withDefault(Constant(0))(); 41 | 42 | TextColumn get title => text()(); 43 | 44 | IntColumn get type => integer().withDefault(Constant(0))(); 45 | 46 | IntColumn get userId => integer().nullable()(); 47 | } 48 | 49 | @UseMoor(tables: [Tasks], daos: [TaskDao]) 50 | class TaskDatabase extends _$TaskDatabase { 51 | // we tell the database where to store the data with this constructor 52 | TaskDatabase() : super(_openConnection()); 53 | 54 | // you should bump this number whenever you change or add a table definition. Migrations 55 | // are covered later in this readme. 56 | @override 57 | int get schemaVersion => 1; 58 | } 59 | 60 | LazyDatabase _openConnection() { 61 | // the LazyDatabase util lets us find the right location for the file async. 62 | return LazyDatabase(() async { 63 | // put the database file, called db.sqlite here, into the documents folder 64 | // for your app. 65 | final dbFolder = await getApplicationDocumentsDirectory(); 66 | final file = File(join(dbFolder.path, 'db.sqlite')); 67 | return VmDatabase(file); 68 | }); 69 | } 70 | -------------------------------------------------------------------------------- /lib/data/remote/app_exceptions.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | import 'package:dio/dio.dart'; 3 | 4 | class AppException implements Exception { 5 | final String _message; 6 | String get message => _message ?? this.runtimeType.toString(); 7 | final int _code; 8 | int get code => _code ?? -1; 9 | AppException([this._message, this._code]); 10 | 11 | String toString() { 12 | return "code:$code--message=$message"; 13 | } 14 | 15 | factory AppException.obtainFromMessage(String error) { 16 | return UnknowException(error); 17 | } 18 | factory AppException.obtainFromException(Exception error) { 19 | if (error is DioError) { 20 | switch (error.type) { 21 | case DioErrorType.DEFAULT: 22 | if (error.error is SocketException) { 23 | // SocketException: Failed host lookup: '***' 24 | // (OS Error: No address associated with hostname, errno = 7) 25 | return NetworkException(error.error.message); 26 | } else { 27 | return UnknowException(error.error.message); 28 | } 29 | break; 30 | case DioErrorType.CONNECT_TIMEOUT: 31 | case DioErrorType.RECEIVE_TIMEOUT: 32 | case DioErrorType.SEND_TIMEOUT: 33 | return NetworkException(error.error.message); 34 | case DioErrorType.RESPONSE: 35 | return HttpException(error.error.message, error.response.statusCode); 36 | case DioErrorType.CANCEL: 37 | return CancelException(error.error.message); 38 | break; 39 | default: 40 | return FetchDataException(error.error.message); 41 | } 42 | } else { 43 | return FetchDataException(error.toString()); 44 | } 45 | } 46 | } 47 | 48 | class HttpException extends AppException { 49 | HttpException([String message, int code]) : super(message, code); 50 | } 51 | 52 | class FetchDataException extends AppException { 53 | FetchDataException([String message]) 54 | : super( 55 | message, 56 | ); 57 | } 58 | 59 | class UnknowException extends AppException { 60 | UnknowException([String message]) 61 | : super( 62 | message, 63 | ); 64 | } 65 | 66 | class CancelException extends AppException { 67 | CancelException([String message]) 68 | : super( 69 | message, 70 | ); 71 | } 72 | 73 | class NetworkException extends AppException { 74 | NetworkException([String message]) 75 | : super( 76 | message, 77 | ); 78 | } 79 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /lib/modules/task/add/add_task_controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:get/get.dart'; 3 | import 'package:todo/data/db/task_database.dart'; 4 | import 'package:todo/data/model/priority.dart'; 5 | import 'package:todo/data/repositories/task_repository.dart'; 6 | import 'package:todo/modules/task/task/task_controller.dart'; 7 | import 'package:todo/utils/extension/date_extension.dart'; 8 | import 'package:todo/utils/extension/get_extension.dart'; 9 | 10 | /// 增加task 11 | class AddTaskController extends GetxController { 12 | final TaskRepository _taskRepository = Get.find(); 13 | 14 | /// 局部更新id 15 | final String updateDateId = 'update_date'; 16 | final String updatePriorityId = 'update_priority'; 17 | 18 | /// 表格key 19 | final formKey = GlobalKey(); 20 | 21 | /// 日期 22 | final dateTimeController = 23 | TextEditingController(text: DateTime.now().format()); 24 | 25 | DateTime _dateTime; 26 | String _title; 27 | String _content; 28 | int _priority; 29 | 30 | @override 31 | void onInit() { 32 | super.onInit(); 33 | _priority = priorities[0]; 34 | _dateTime = DateTime.now(); 35 | } 36 | 37 | void saveTitle(String value) { 38 | _title = value; 39 | } 40 | 41 | void saveContent(String value) { 42 | _content = value; 43 | } 44 | 45 | void changePriority(String value) { 46 | _priority = priorities[prioritiesStr.indexOf(value)]; 47 | } 48 | 49 | void handleDatePicker() async { 50 | final datePick = await showDatePicker( 51 | context: Get.context, 52 | firstDate: DateTime(2000), 53 | initialDate: _dateTime, 54 | lastDate: DateTime(2100)); 55 | if (datePick != null && datePick != _dateTime) { 56 | _dateTime = datePick; 57 | dateTimeController.text = _dateTime.format(); 58 | update([updateDateId]); 59 | } 60 | } 61 | 62 | void submit() async { 63 | if (formKey.currentState.validate()) { 64 | formKey.currentState.save(); 65 | try { 66 | Get.loading(); 67 | Task task = await _taskRepository.addTask(_title, 68 | content: _content, date: _dateTime.format(), priority: _priority); 69 | Get.dismiss(); 70 | TaskController controller = Get.find(); 71 | controller.addNewTask(task); 72 | Get.back(); 73 | } catch (e) { 74 | print('submit==$e'); 75 | Get.dismiss(); 76 | Get.snackbar('Error', e.toString()); 77 | } 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /lib/modules/task/edit/edit_task_controller.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: your name 3 | * @Date: 2020-12-09 20:10:32 4 | * @LastEditTime: 2020-12-13 01:55:06 5 | * @LastEditors: Please set LastEditors 6 | * @Description: In User Settings Edit 7 | * @FilePath: /todo/lib/modules/task/edit/edit_task_controller.dart 8 | */ 9 | import 'package:flutter/material.dart'; 10 | import 'package:get/get.dart'; 11 | import 'package:todo/data/db/task_database.dart'; 12 | import 'package:todo/data/model/priority.dart'; 13 | import 'package:todo/data/repositories/task_repository.dart'; 14 | import 'package:todo/modules/task/task/task_controller.dart'; 15 | import 'package:todo/utils/extension/date_extension.dart'; 16 | import 'package:todo/utils/extension/get_extension.dart'; 17 | 18 | /// 增加task 19 | class EditTaskController extends GetxController { 20 | final TaskRepository _taskRepository = Get.find(); 21 | Task task = Get.arguments; 22 | 23 | /// 局部更新id 24 | final String updateDateId = 'update_date'; 25 | final String updatePriorityId = 'update_priority'; 26 | 27 | /// 表格key 28 | final formKey = GlobalKey(); 29 | 30 | /// 日期 31 | TextEditingController dateTimeController; 32 | 33 | DateTime _dateTime; 34 | 35 | @override 36 | void onInit() { 37 | super.onInit(); 38 | _dateTime = DateTime.parse(task.dateStr); 39 | dateTimeController = TextEditingController(text: task.dateStr); 40 | } 41 | 42 | void saveTitle(String value) { 43 | print('saveTitle=' + value); 44 | task.title = value; 45 | } 46 | 47 | void saveContent(String value) { 48 | task.content = value; 49 | } 50 | 51 | void changePriority(String value) { 52 | task.priority = priorities[prioritiesStr.indexOf(value)]; 53 | } 54 | 55 | void handleDatePicker() async { 56 | final datePick = await showDatePicker( 57 | context: Get.context, 58 | firstDate: DateTime(2000), 59 | initialDate: _dateTime, 60 | lastDate: DateTime(2100)); 61 | if (datePick != null && datePick != _dateTime) { 62 | _dateTime = datePick; 63 | task.dateStr = _dateTime.format(); 64 | dateTimeController.text = task.dateStr; 65 | update([updateDateId]); 66 | } 67 | } 68 | 69 | void submit() async { 70 | if (formKey.currentState.validate()) { 71 | formKey.currentState.save(); 72 | try { 73 | Get.loading(); 74 | await _taskRepository.updateTask(task); 75 | Get.dismiss(); 76 | // 刷新列表页 77 | Get.find().update(); 78 | // controller.updateTask(task); 79 | Get.back(); 80 | } catch (e) { 81 | print(e); 82 | Get.dismiss(); 83 | Get.snackbar('Error', e.toString()); 84 | } 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /lib/data/db/task_dao.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: your name 3 | * @Date: 2020-12-10 23:05:53 4 | * @LastEditTime: 2020-12-12 12:48:59 5 | * @LastEditors: Please set LastEditors 6 | * @Description: In User Settings Edit 7 | * @FilePath: /todo/lib/data/db/task_dao.dart 8 | */ 9 | import 'package:moor/moor.dart'; 10 | import 'package:todo/data/db/task_database.dart'; 11 | part 'task_dao.g.dart'; 12 | 13 | @UseDao(tables: [Tasks]) 14 | class TaskDao extends DatabaseAccessor with _$TaskDaoMixin { 15 | TaskDao(TaskDatabase db) : super(db); 16 | 17 | /// 获取全部 18 | Future> get getAllTasks => select(tasks).get(); 19 | 20 | ///imit查询来限制返回的结果数量 21 | ///offset偏移量 22 | Future> getTasks(int limit, {int offset}) { 23 | return (select(tasks)..limit(limit, offset: offset)).get(); 24 | } 25 | 26 | ///imit查询来限制返回的结果数量 27 | ///offset偏移量 28 | Future> getTasksWithDateStr(String dateStr) { 29 | return (select(tasks)..where((e) => e.dateStr.equals(dateStr))).get(); 30 | } 31 | 32 | /// 获取单个数据 33 | /// 没必要用list 34 | Future getTaskById(int id) { 35 | return (select(tasks)..where((t) => t.id.equals(id))).getSingle(); 36 | } 37 | 38 | Future updateTask(Task entry) { 39 | TasksCompanion(); 40 | 41 | return update(tasks).replace(entry); 42 | } 43 | 44 | Future createOrUpdateUser(String title, 45 | {String content, String date, int type = 0, int priority = 0}) { 46 | return into(tasks).insertOnConflictUpdate(TasksCompanion( 47 | title: Value(title), 48 | content: Value(content), 49 | dateStr: Value(date), 50 | type: Value(type), 51 | priority: Value(priority), 52 | )); 53 | } 54 | 55 | Future createTask(TasksCompanion task) async { 56 | var id = await into(tasks).insertOnConflictUpdate(task); 57 | return getTaskById(id); 58 | } 59 | 60 | /// 批量插入 61 | Future insertMultipleTasks(List entries) async { 62 | await batch((batch) { 63 | batch.insertAll(tasks, entries); 64 | }); 65 | } 66 | 67 | Future deleteTaskById(int id) { 68 | return (delete(tasks)..where((t) => t.id.equals(id))).go(); 69 | } 70 | 71 | Future deleteTask(Task entry) { 72 | return delete(tasks).delete(entry); 73 | } 74 | 75 | Future modifyStatusByid(int id, int status) async { 76 | // into(tasks).up 77 | Task task = await getTaskById(id); 78 | task.copyWith( 79 | status: status, 80 | ); 81 | await updateTask(task); 82 | return task; 83 | } 84 | 85 | Future modifyTask(Task task) { 86 | return update(tasks).replace(task); 87 | } 88 | 89 | /// 表中数据改变,会发生一个流 90 | Stream> watchEntriesInCategory() { 91 | return select(tasks).watch(); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /lib/modules/task/task/components/body.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: your name 3 | * @Date: 2020-12-09 20:10:32 4 | * @LastEditTime: 2020-12-12 15:36:06 5 | * @LastEditors: Please set LastEditors 6 | * @Description: In User Settings Edit 7 | * @FilePath: /todo/lib/modules/task/task/components/body.dart 8 | */ 9 | import 'package:flutter/material.dart'; 10 | import 'package:flutter_slidable/flutter_slidable.dart'; 11 | import 'package:get/get.dart'; 12 | import 'package:pull_to_refresh/pull_to_refresh.dart'; 13 | import 'package:todo/data/db/task_database.dart'; 14 | import 'package:todo/modules/task/task/components/task_widget.dart'; 15 | import 'package:todo/routes/app_pages.dart'; 16 | import 'package:todo/widgets/start_card.dart'; 17 | import '../task_controller.dart'; 18 | 19 | class Body extends GetView { 20 | final SlidableController slidableController = SlidableController(); 21 | 22 | Widget _buildItem(BuildContext context, int index) { 23 | Task task = controller.tasks[index]; 24 | return Slidable( 25 | controller: slidableController, 26 | actionPane: SlidableDrawerActionPane(), 27 | actionExtentRatio: 0.2, 28 | child: TaskWidget( 29 | task: task, 30 | onItemClick: () => Get.toNamed(Routes.TASK_DETAILS, arguments: task), 31 | onCheckBoxChanged: (b) { 32 | task.status = b ? 1 : 0; 33 | controller.modifyTaskStatus(task); 34 | }, 35 | ), 36 | secondaryActions: [ 37 | IconSlideAction( 38 | caption: "Edit", 39 | color: Theme.of(context).accentColor, 40 | icon: Icons.edit, 41 | onTap: () { 42 | Get.toNamed(Routes.TASK_EDIT, arguments: task); 43 | }, 44 | ), 45 | IconSlideAction( 46 | caption: "Delete", 47 | color: Colors.red, 48 | icon: Icons.delete, 49 | onTap: () => controller.deleteTask(index)), 50 | ], 51 | ); 52 | } 53 | 54 | @override 55 | Widget build(BuildContext context) { 56 | return GetBuilder( 57 | init: controller, 58 | builder: (_) { 59 | return SmartRefresher( 60 | header: MaterialClassicHeader(), 61 | controller: _.refreshController, 62 | enablePullDown: true, 63 | enablePullUp: true, 64 | onLoading: _.onLoadMore, 65 | onRefresh: _.onRefresh, 66 | child: _.tasks.isEmpty 67 | ? TaskCardWidget() 68 | : ListView.builder( 69 | itemCount: _.tasks.length, 70 | itemBuilder: (context, index) { 71 | return _buildItem(context, index); 72 | }, 73 | ), 74 | ); 75 | }, 76 | ); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /lib/routes/app_pages.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: your name 3 | * @Date: 2020-12-08 20:57:12 4 | * @LastEditTime: 2020-12-12 14:37:43 5 | * @LastEditors: Please set LastEditors 6 | * @Description: In User Settings Edit 7 | * @FilePath: /todo/lib/routes/app_pages.dart 8 | */ 9 | /* 10 | * @Author: your name 11 | * @Date: 2020-12-08 20:57:12 12 | * @LastEditTime: 2020-12-08 21:23:35 13 | * @LastEditors: Please set LastEditors 14 | * @Description: In User Settings Edit 15 | * @FilePath: /todo/lib/routes/app_pages.dart 16 | */ 17 | import 'package:get/get.dart'; 18 | import 'package:todo/modules/login/login_binding.dart'; 19 | import 'package:todo/modules/login/login_page.dart'; 20 | import 'package:todo/modules/profile/profile_page.dart'; 21 | import 'package:todo/modules/signup/sign_up_binding.dart'; 22 | import 'package:todo/modules/signup/sign_up_page.dart'; 23 | import 'package:todo/modules/splash/splash_binding.dart'; 24 | import 'package:todo/modules/splash/splash_page.dart'; 25 | import 'package:todo/modules/task/add/add_task_binding.dart'; 26 | import 'package:todo/modules/task/add/add_task_page.dart'; 27 | import 'package:todo/modules/task/details/task_details.dart'; 28 | import 'package:todo/modules/task/edit/edit_task_binding.dart'; 29 | import 'package:todo/modules/task/edit/edit_task_page.dart'; 30 | import 'package:todo/modules/task/monthly/monthly_binding.dart'; 31 | import 'package:todo/modules/task/monthly/monthly_page.dart'; 32 | import 'package:todo/modules/task/task/task_binding.dart'; 33 | import 'package:todo/modules/task/task/task_page.dart'; 34 | part './app_routes.dart'; 35 | 36 | abstract class AppPages { 37 | static final pages = [ 38 | GetPage( 39 | name: Routes.LOGIN, 40 | page: () => LoginPage(), 41 | binding: LoginPageBinding(), 42 | ), 43 | GetPage( 44 | name: Routes.SPLASH, 45 | page: () => SplashPage(), 46 | binding: SplashBinding(), 47 | ), 48 | GetPage( 49 | name: Routes.SIGN_UP, 50 | page: () => SignUpPage(), 51 | binding: SiginUpBinding(), 52 | ), 53 | GetPage( 54 | name: Routes.TASK, 55 | page: () => TaskPage(), 56 | binding: TaskBinding(), 57 | ), 58 | GetPage( 59 | name: Routes.TASK_ADD, 60 | page: () => AddTaskPage(), 61 | binding: AddTaskBinding(), 62 | ), 63 | GetPage( 64 | name: Routes.TASK_DETAILS, 65 | page: () => TaskDetailsPage(), 66 | ), 67 | GetPage( 68 | name: Routes.TASK_EDIT, 69 | page: () => EditTaskPage(), 70 | binding: EditTaskBinding(), 71 | ), 72 | GetPage( 73 | name: Routes.TASK_MOTHLY, 74 | page: () => MonthlyPage(), 75 | binding: MonthlyBinding(), 76 | ), 77 | GetPage( 78 | name: Routes.PROFILE, 79 | page: () => ProfilePage(), 80 | ), 81 | ]; 82 | } 83 | -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 8 | 9 | 10 | 11 | 12 | 13 | 17 | 24 | 28 | 32 | 37 | 41 | 42 | 43 | 44 | 45 | 46 | 48 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /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/modules/signup/components/body.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: your name 3 | * @Date: 2020-12-09 20:10:32 4 | * @LastEditTime: 2020-12-13 02:43:48 5 | * @LastEditors: Please set LastEditors 6 | * @Description: In User Settings Edit 7 | * @FilePath: /todo/lib/modules/signup/components/body.dart 8 | */ 9 | import 'package:flutter/material.dart'; 10 | import 'package:get/get.dart'; 11 | import 'package:todo/modules/login/components/alread_have_an_account_check.dart'; 12 | import 'package:todo/modules/login/components/round_paaword_field.dart'; 13 | import 'package:todo/modules/login/components/rounded_button.dart'; 14 | import 'package:todo/modules/login/components/rounded_input_field.dart'; 15 | import 'package:todo/routes/app_pages.dart'; 16 | import '../sign_up_controller.dart'; 17 | 18 | class Body extends GetView { 19 | @override 20 | Widget build(BuildContext context) { 21 | return SingleChildScrollView( 22 | child: Column( 23 | crossAxisAlignment: CrossAxisAlignment.center, 24 | children: [ 25 | Container( 26 | width: Get.width, 27 | padding: EdgeInsets.fromLTRB(15.0, 115.0, 0.0, 0.0), 28 | child: Text('Create', 29 | style: Theme.of(context).textTheme.headline2.copyWith( 30 | fontWeight: FontWeight.bold, 31 | color: Theme.of(context).primaryColor, 32 | )), 33 | ), 34 | Row( 35 | crossAxisAlignment: CrossAxisAlignment.center, 36 | children: [ 37 | Container( 38 | padding: EdgeInsets.fromLTRB(15.0, 15.0, 0.0, 0.0), 39 | child: Text('Account', 40 | style: Theme.of(context).textTheme.headline2.copyWith( 41 | fontWeight: FontWeight.bold, 42 | color: Theme.of(context).primaryColor, 43 | )), 44 | ), 45 | Container( 46 | width: 22, 47 | height: 22, 48 | decoration: BoxDecoration( 49 | shape: BoxShape.circle, 50 | color: Theme.of(context).primaryColorLight, 51 | ), 52 | ) 53 | ], 54 | ), 55 | SizedBox( 56 | height: 60, 57 | ), 58 | RoundedInputField( 59 | hintText: 'Your Email', 60 | icon: Icons.person, 61 | onChanged: controller.onUsernameChanged, 62 | ), 63 | RoundedPasswordField( 64 | onChanged: controller.onPasswordChanged, 65 | ), 66 | RoundedPasswordField( 67 | onChanged: controller.onRePasswordChanged, 68 | ), 69 | SizedBox( 70 | height: 8, 71 | ), 72 | RoundedButton( 73 | text: 'SINGUP', 74 | onPressed: controller.sumit, 75 | ), 76 | SizedBox( 77 | height: 12, 78 | ), 79 | AlreadHaveAnAccoutCheck( 80 | login: false, 81 | onTap: () => Get.offNamed(Routes.LOGIN), 82 | ), 83 | ], 84 | ), 85 | ); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /lib/modules/task/task/components/task_widget.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: your name 3 | * @Date: 2020-12-09 20:10:32 4 | * @LastEditTime: 2020-12-12 13:26:24 5 | * @LastEditors: Please set LastEditors 6 | * @Description: In User Settings Edit 7 | * @FilePath: /todo/lib/modules/task/task/components/task_item.dart 8 | */ 9 | import 'package:flutter/material.dart'; 10 | import 'package:todo/data/db/task_database.dart'; 11 | import 'package:todo/data/model/priority.dart'; 12 | import 'package:todo/modules/task/task/components/round_check_box.dart'; 13 | 14 | /// task 展示控件 15 | class TaskWidget extends StatelessWidget { 16 | final Task task; 17 | final VoidCallback onItemClick; 18 | final ValueChanged onCheckBoxChanged; 19 | 20 | const TaskWidget({ 21 | Key key, 22 | @required this.task, 23 | this.onItemClick, 24 | this.onCheckBoxChanged, 25 | }) : super(key: key); 26 | 27 | @override 28 | Widget build(BuildContext context) { 29 | return Container( 30 | height: 80, 31 | margin: EdgeInsets.symmetric(horizontal: 20, vertical: 5), 32 | decoration: BoxDecoration( 33 | color: Colors.white, 34 | boxShadow: [ 35 | BoxShadow( 36 | color: Colors.black.withOpacity(0.03), 37 | offset: Offset(0, 2), 38 | blurRadius: 5, 39 | spreadRadius: 1) 40 | ], 41 | ), 42 | child: _buildRow(context), 43 | ); 44 | } 45 | 46 | Widget _buildRow(BuildContext context) { 47 | return InkWell( 48 | onTap: onItemClick, 49 | child: Row( 50 | children: [ 51 | Container( 52 | margin: EdgeInsets.symmetric(horizontal: 20), 53 | width: 25, 54 | height: 25, 55 | child: RoundCheckBox( 56 | value: task.status == 1, 57 | size: Size(25, 25), 58 | color: priorityColor[task.priority], 59 | onChanged: onCheckBoxChanged, 60 | ), 61 | ), 62 | Column( 63 | crossAxisAlignment: CrossAxisAlignment.start, 64 | mainAxisAlignment: MainAxisAlignment.center, 65 | children: [ 66 | Text( 67 | task.title, 68 | style: Theme.of(context).textTheme.subtitle1.copyWith( 69 | fontSize: 18, 70 | decoration: task.status == 1 71 | ? TextDecoration.lineThrough 72 | : TextDecoration.none, 73 | ), 74 | ), 75 | SizedBox( 76 | height: 5, 77 | ), 78 | Text(task.dateStr, 79 | style: Theme.of(context).textTheme.caption.copyWith( 80 | fontSize: 14, 81 | decoration: task.status == 1 82 | ? TextDecoration.lineThrough 83 | : TextDecoration.none, 84 | )), 85 | ], 86 | ), 87 | Spacer(), 88 | Container( 89 | width: 5, 90 | height: 50, 91 | color: priorityColor[task.priority], 92 | ), 93 | ], 94 | ), 95 | ); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /lib/modules/login/components/body.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: your name 3 | * @Date: 2020-12-08 20:57:12 4 | * @LastEditTime: 2020-12-13 02:43:24 5 | * @LastEditors: Please set LastEditors 6 | * @Description: In User Settings Edit 7 | * @FilePath: /todo/lib/modules/login/components/body.dart 8 | */ 9 | import 'package:flutter/material.dart'; 10 | import 'package:get/get.dart'; 11 | import 'package:todo/modules/login/components/round_paaword_field.dart'; 12 | import 'package:todo/modules/login/components/rounded_button.dart'; 13 | import 'package:todo/modules/login/components/rounded_input_field.dart'; 14 | import 'package:todo/routes/app_pages.dart'; 15 | import '../login_controller.dart'; 16 | import 'alread_have_an_account_check.dart'; 17 | import 'or_divider.dart'; 18 | 19 | class Body extends GetView { 20 | @override 21 | Widget build(BuildContext context) { 22 | return SingleChildScrollView( 23 | child: Column( 24 | crossAxisAlignment: CrossAxisAlignment.center, 25 | children: [ 26 | Container( 27 | width: Get.width, 28 | padding: EdgeInsets.fromLTRB(15.0, 115.0, 0.0, 0.0), 29 | child: Text('Welcome', 30 | style: Theme.of(context).textTheme.headline2.copyWith( 31 | fontWeight: FontWeight.bold, 32 | color: Theme.of(context).primaryColor, 33 | )), 34 | ), 35 | Row( 36 | crossAxisAlignment: CrossAxisAlignment.center, 37 | children: [ 38 | Container( 39 | padding: EdgeInsets.fromLTRB(15.0, 15.0, 0.0, 0.0), 40 | child: Text('Back', 41 | style: Theme.of(context).textTheme.headline2.copyWith( 42 | fontWeight: FontWeight.bold, 43 | color: Theme.of(context).primaryColor, 44 | )), 45 | ), 46 | Container( 47 | width: 22, 48 | height: 22, 49 | decoration: BoxDecoration( 50 | shape: BoxShape.circle, 51 | color: Theme.of(context).primaryColorLight, 52 | ), 53 | ) 54 | ], 55 | ), 56 | SizedBox( 57 | height: 60, 58 | ), 59 | RoundedInputField( 60 | hintText: 'Your Email', 61 | icon: Icons.person, 62 | onChanged: controller.onUsernameChanged, 63 | ), 64 | RoundedPasswordField( 65 | onChanged: controller.onPasswordChanged, 66 | ), 67 | SizedBox( 68 | height: 8, 69 | ), 70 | RoundedButton( 71 | text: 'LOGIN', 72 | onPressed: controller.sumit, 73 | ), 74 | SizedBox( 75 | height: 12, 76 | ), 77 | AlreadHaveAnAccoutCheck( 78 | login: true, 79 | onTap: () => Get.toNamed(Routes.SIGN_UP), 80 | ), 81 | SizedBox( 82 | height: 24, 83 | ), 84 | OrDivider(), 85 | SizedBox( 86 | height: 24, 87 | ), 88 | RoundedButton( 89 | text: 'SKIP SIGN', 90 | onPressed: () => Get.offNamed(Routes.TASK), 91 | ), 92 | ], 93 | ), 94 | ); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: todo 2 | description: A new Flutter project. 3 | 4 | # The following line prevents the package from being accidentally published to 5 | # pub.dev using `pub publish`. This is preferred for private packages. 6 | publish_to: "none" # Remove this line if you wish to publish to pub.dev 7 | 8 | # The following defines the version and build number for your application. 9 | # A version number is three numbers separated by dots, like 1.2.43 10 | # followed by an optional build number separated by a +. 11 | # Both the version and the builder number may be overridden in flutter 12 | # build by specifying --build-name and --build-number, respectively. 13 | # In Android, build-name is used as versionName while build-number used as versionCode. 14 | # Read more about Android versioning at https://developer.android.com/studio/publish/versioning 15 | # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. 16 | # Read more about iOS versioning at 17 | # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html 18 | version: 1.0.0+1 19 | 20 | environment: 21 | sdk: ">=2.7.0 <3.0.0" 22 | 23 | dependencies: 24 | flutter: 25 | sdk: flutter 26 | cookie_jar: ^1.0.1 27 | cupertino_icons: ^1.0.0 28 | date_format: ^1.0.9 29 | dio: ^3.0.10 30 | dio_cookie_manager: ^1.0.0 31 | dio_http_cache: ^0.2.11 32 | flutter_slidable: ^0.5.7 33 | get: ^3.21.2 34 | google_fonts: ^1.1.1 35 | moor: 36 | sqlite3_flutter_libs: 37 | path: ^1.7.0 38 | path_provider: ^1.6.24 39 | pull_to_refresh: ^1.6.3 40 | shared_preferences: ^0.5.12+4 41 | table_calendar: ^2.3.1 42 | 43 | dev_dependencies: 44 | flutter_test: 45 | sdk: flutter 46 | build_runner: ^1.10.7 47 | moor_generator: 48 | 49 | # For information on the generic Dart part of this file, see the 50 | # following page: https://dart.dev/tools/pub/pubspec 51 | # The following section is specific to Flutter. 52 | flutter: 53 | # The following line ensures that the Material Icons font is 54 | # included with your application, so that you can use the icons in 55 | # the material Icons class. 56 | uses-material-design: true 57 | 58 | # To add assets to your application, add an assets section, like this: 59 | assets: 60 | - assets/images/3.0x/ 61 | # assets-generator-begin 62 | # assets/images/3.0x/* 63 | # assets-generator-end 64 | # An image asset can refer to one or more resolution-specific "variants", see 65 | # https://flutter.dev/assets-and-images/#resolution-aware. 66 | # For details regarding adding assets from package dependencies, see 67 | # https://flutter.dev/assets-and-images/#from-packages 68 | # To add custom fonts to your application, add a fonts section here, 69 | # in this "flutter" section. Each entry in this list should have a 70 | # "family" key with the font family name, and a "fonts" key with a 71 | # list giving the asset and other descriptors for the font. For 72 | # example: 73 | # fonts: 74 | # - family: Schyler 75 | # fonts: 76 | # - asset: fonts/Schyler-Regular.ttf 77 | # - asset: fonts/Schyler-Italic.ttf 78 | # style: italic 79 | # - family: Trajan Pro 80 | # fonts: 81 | # - asset: fonts/TrajanPro.ttf 82 | # - asset: fonts/TrajanPro_Bold.ttf 83 | # weight: 700 84 | # 85 | # For details regarding fonts from package dependencies, 86 | # see https://flutter.dev/custom-fonts/#from-packages 87 | -------------------------------------------------------------------------------- /lib/data/remote/dio_client.dart: -------------------------------------------------------------------------------- 1 | import 'package:dio/dio.dart'; 2 | import 'package:todo/data/remote/app_dio.dart'; 3 | import 'package:todo/data/remote/app_response.dart'; 4 | 5 | class DioClient { 6 | AppDio _dio = AppDio.getInstance(); 7 | Future get( 8 | String uri, { 9 | Map queryParameters, 10 | Options options, 11 | CancelToken cancelToken, 12 | ProgressCallback onReceiveProgress, 13 | }) async { 14 | try { 15 | var response = await _dio.get( 16 | uri, 17 | queryParameters: queryParameters, 18 | options: options, 19 | cancelToken: cancelToken, 20 | onReceiveProgress: onReceiveProgress, 21 | ); 22 | return AppResponse.obtain(response); 23 | } catch (e) { 24 | throw e; 25 | } 26 | } 27 | 28 | Future post( 29 | String uri, { 30 | data, 31 | Map queryParameters, 32 | Options options, 33 | CancelToken cancelToken, 34 | ProgressCallback onSendProgress, 35 | ProgressCallback onReceiveProgress, 36 | }) async { 37 | try { 38 | var response = await _dio.post( 39 | uri, 40 | data: data, 41 | queryParameters: queryParameters, 42 | options: options, 43 | cancelToken: cancelToken, 44 | onSendProgress: onSendProgress, 45 | onReceiveProgress: onReceiveProgress, 46 | ); 47 | return AppResponse.obtain(response); 48 | } catch (e) { 49 | throw e; 50 | } 51 | } 52 | 53 | Future patch( 54 | String uri, { 55 | data, 56 | Map queryParameters, 57 | Options options, 58 | CancelToken cancelToken, 59 | ProgressCallback onSendProgress, 60 | ProgressCallback onReceiveProgress, 61 | }) async { 62 | try { 63 | var response = await _dio.patch( 64 | uri, 65 | data: data, 66 | queryParameters: queryParameters, 67 | options: options, 68 | cancelToken: cancelToken, 69 | onSendProgress: onSendProgress, 70 | onReceiveProgress: onReceiveProgress, 71 | ); 72 | return AppResponse.obtain(response); 73 | } catch (e) { 74 | throw e; 75 | } 76 | } 77 | 78 | Future delete( 79 | String uri, { 80 | data, 81 | Map queryParameters, 82 | Options options, 83 | CancelToken cancelToken, 84 | }) async { 85 | try { 86 | var response = await _dio.delete( 87 | uri, 88 | data: data, 89 | queryParameters: queryParameters, 90 | options: options, 91 | cancelToken: cancelToken, 92 | ); 93 | return AppResponse.obtain(response); 94 | } on FormatException catch (_) { 95 | throw FormatException("Unable to process the data"); 96 | } catch (e) { 97 | throw e; 98 | } 99 | } 100 | 101 | Future put( 102 | String uri, { 103 | data, 104 | Map queryParameters, 105 | Options options, 106 | CancelToken cancelToken, 107 | }) async { 108 | try { 109 | var response = await _dio.put( 110 | uri, 111 | data: data, 112 | queryParameters: queryParameters, 113 | options: options, 114 | cancelToken: cancelToken, 115 | ); 116 | return AppResponse.obtain(response); 117 | } catch (e) { 118 | throw e; 119 | } 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /lib/modules/task/monthly/components/body.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: your name 3 | * @Date: 2020-12-09 20:10:32 4 | * @LastEditTime: 2020-12-12 14:00:22 5 | * @LastEditors: Please set LastEditors 6 | * @Description: In User Settings Edit 7 | * @FilePath: /todo/lib/modules/task/monthly/components/body.dart 8 | */ 9 | import 'package:flutter/material.dart'; 10 | import 'package:get/get.dart'; 11 | import 'package:table_calendar/table_calendar.dart'; 12 | import 'package:todo/modules/task/task/components/task_widget.dart'; 13 | import 'package:todo/routes/app_pages.dart'; 14 | import '../monthly_controller.dart'; 15 | 16 | class Body extends GetView { 17 | @override 18 | Widget build(BuildContext context) { 19 | return Column( 20 | children: [ 21 | TableCalendar( 22 | onDaySelected: (DateTime day, _, __) { 23 | controller.selectedDate(day); 24 | }, 25 | calendarController: controller.calendarController, 26 | startingDayOfWeek: StartingDayOfWeek.monday, 27 | initialCalendarFormat: CalendarFormat.week, 28 | calendarStyle: CalendarStyle( 29 | selectedColor: Theme.of(context).accentColor, 30 | ), 31 | ), 32 | SizedBox( 33 | height: 20, 34 | ), 35 | Expanded( 36 | child: Container( 37 | padding: EdgeInsets.only(left: 30, top: 30), 38 | width: Get.width, 39 | decoration: BoxDecoration( 40 | color: Theme.of(context).accentColor, 41 | borderRadius: BorderRadius.only( 42 | topLeft: Radius.circular(50), 43 | topRight: Radius.circular(50))), 44 | child: Column( 45 | crossAxisAlignment: CrossAxisAlignment.start, 46 | children: [ 47 | Text('Today', 48 | style: Theme.of(context).textTheme.headline5.copyWith( 49 | color: Colors.white, fontWeight: FontWeight.bold)), 50 | Expanded( 51 | child: GetBuilder(builder: (_) { 52 | return ListView.builder( 53 | itemCount: controller.tasks.length, 54 | itemBuilder: (context, index) { 55 | var task = controller.tasks[index]; 56 | return Container( 57 | margin: EdgeInsets.only(top: 10, right: 20), 58 | decoration: BoxDecoration( 59 | color: Colors.white, 60 | borderRadius: 61 | BorderRadius.all(Radius.circular(10))), 62 | child: TaskWidget( 63 | task: task, 64 | onItemClick: () => Get.toNamed( 65 | Routes.TASK_DETAILS, 66 | arguments: task), 67 | onCheckBoxChanged: (b) { 68 | task.status = b ? 1 : 0; 69 | controller.modifyTaskStatus(task); 70 | }, 71 | ), 72 | ); 73 | }, 74 | ); 75 | }), 76 | ) 77 | ], 78 | )), 79 | ) 80 | ], 81 | ); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /lib/data/api/task_api.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | /* 3 | * @Author: your name 4 | * @Date: 2020-12-08 20:57:12 5 | * @LastEditTime: 2020-12-12 12:15:10 6 | * @LastEditors: Please set LastEditors 7 | * @Description: In User Settings Edit 8 | * @FilePath: /todo/lib/data/api/task_api.dart 9 | */ 10 | import 'package:get/get.dart'; 11 | import 'package:todo/data/db/task_database.dart'; 12 | import 'package:todo/data/model/task_model.dart'; 13 | import 'package:todo/data/remote/app_response.dart'; 14 | import 'package:todo/data/remote/dio_client.dart'; 15 | 16 | class TaskApi { 17 | final DioClient _dio = Get.find(); 18 | 19 | final String addTaskPath = "/lg/todo/add/json"; 20 | 21 | /// 页码从1开始,拼接在url上 22 | // status 状态, 1-完成;0未完成; 默认全部展示; 23 | // type 创建时传入的类型, 默认全部展示 24 | // priority 创建时传入的优先级;默认全部展示 25 | // orderby 1:完成日期顺序;2.完成日期逆序;3.创建日期顺序;4.创建日期逆序(默认); 26 | Future getTasks({int type, int pageNum = 1}) async { 27 | AppResponse appResponse = await _dio.get("/lg/todo/v2/list/$pageNum/json"); 28 | if (appResponse.ok) { 29 | return TaskModel.fromJson(appResponse.data); 30 | } else { 31 | throw appResponse.error; 32 | } 33 | } 34 | 35 | // title: 新增标题(必须) 36 | // content: 新增详情(必须) 37 | // date: 2018-08-01 预定完成时间(不传默认当天,建议传) 38 | // type: 大于0的整数(可选); 39 | // priority 大于0的整数(可选); 40 | Future addTask(String title, 41 | {String content, String date, int type = 0, int priority = 0}) async { 42 | AppResponse appResponse = await _dio.post(addTaskPath, queryParameters: { 43 | 'title': title, 44 | 'content': content, 45 | 'date': date, 46 | 'type': type, 47 | 'priority': priority 48 | }); 49 | if (appResponse.ok) { 50 | print(appResponse.data); 51 | return Task.fromJson(appResponse.data); 52 | } else { 53 | throw appResponse.error; 54 | } 55 | } 56 | 57 | // title: 新增标题(必须) 58 | // content: 新增详情(必须) 59 | // date: 2018-08-01 预定完成时间(不传默认当天,建议传) 60 | // type: 大于0的整数(可选); 61 | // priority 大于0的整数(可选); 62 | Future updateTask( 63 | {@required int id, 64 | @required String title, 65 | @required String date, 66 | String content, 67 | int status, 68 | int type, 69 | int priority}) async { 70 | AppResponse appResponse = 71 | await _dio.post('/lg/todo/update/$id/json', queryParameters: { 72 | 'id': id, 73 | 'title': title, 74 | 'content': content, 75 | 'date': date, 76 | 'status': status, 77 | 'type': type, 78 | 'priority': priority 79 | }); 80 | if (appResponse.ok) { 81 | print(appResponse.data); 82 | return Task.fromJson(appResponse.data); 83 | } else { 84 | throw appResponse.error; 85 | } 86 | } 87 | 88 | // title: 新增标题(必须) 89 | // content: 新增详情(必须) 90 | // date: 2018-08-01 预定完成时间(不传默认当天,建议传) 91 | // type: 大于0的整数(可选); 92 | // priority 大于0的整数(可选); 93 | Future deleteTask(int id) async { 94 | AppResponse appResponse = await _dio.post("/lg/todo/delete/$id/json"); 95 | if (appResponse.ok) { 96 | return appResponse.ok; 97 | } else { 98 | throw appResponse.error; 99 | } 100 | } 101 | 102 | Future modifyTaskStatus(int id, int status) async { 103 | AppResponse appResponse = await _dio 104 | .post("/lg/todo/done/$id/json", queryParameters: {"status": status}); 105 | if (appResponse.ok) { 106 | return Task.fromJson(appResponse.data); 107 | } else { 108 | throw appResponse.error; 109 | } 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /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/data/repositories/task_repository.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: your name 3 | * @Date: 2020-12-08 20:57:12 4 | * @LastEditTime: 2020-12-13 01:34:47 5 | * @LastEditors: Please set LastEditors 6 | * @Description: In User Settings Edit 7 | * @FilePath: /todo/lib/data/repositories/task_repository.dart 8 | */ 9 | import 'package:get/get.dart' hide Value; 10 | import 'package:moor/moor.dart'; 11 | import 'package:todo/data/api/task_api.dart'; 12 | import 'package:todo/data/db/task_dao.dart'; 13 | import 'package:todo/data/db/task_database.dart'; 14 | import 'package:todo/data/model/task_model.dart'; 15 | import 'package:todo/data/providers/login_provider.dart'; 16 | 17 | class TaskRepository { 18 | final TaskApi api = Get.find(); 19 | final TaskDao taskDao = Get.find(); 20 | final LoginProvider loginProvider = Get.find(); 21 | 22 | Future getTask({int pageNum = 1}) async { 23 | if (loginProvider.isLogin()) { 24 | try { 25 | TaskModel model = await api.getTasks(pageNum: pageNum); 26 | if (model.datas.isNotEmpty == true) 27 | await taskDao.insertMultipleTasks(model.datas); 28 | } catch (e) { 29 | print('TaskRepository==getTask:$e'); 30 | } 31 | } 32 | var data = await taskDao.getTasks(20, offset: (pageNum - 1) * 20); 33 | TaskModel model = TaskModel( 34 | curPage: pageNum, 35 | datas: data, 36 | over: ((data?.length ?? 0 < 20) == true)); 37 | return model; 38 | } 39 | 40 | Future addTask(String title, 41 | {String content, String date, int type = 0, int priority = 0}) async { 42 | Task task; 43 | if (loginProvider.isLogin()) { 44 | try { 45 | task = await api.addTask(title, 46 | content: content, date: date, type: type, priority: priority); 47 | } catch (e) { 48 | print('TaskRepository==addTask:$e'); 49 | } 50 | } 51 | TasksCompanion tasksCompanion = TasksCompanion( 52 | title: Value(task?.title ?? title), 53 | content: Value(task?.content ?? content), 54 | id: task?.id==null ? Value.absent():Value(task?.id), 55 | date: Value(task?.date ?? 56 | DateTime.parse(task?.dateStr ?? date).microsecondsSinceEpoch), 57 | dateStr: Value(task?.dateStr ?? date), 58 | type: Value(task?.type ?? type), 59 | priority: Value(task?.priority ?? priority), 60 | ); 61 | return await taskDao.createTask(tasksCompanion); 62 | } 63 | 64 | Future deleteTask(int id) async { 65 | if (loginProvider.isLogin()) { 66 | try { 67 | await api.deleteTask(id); 68 | } catch (e) { 69 | print('TaskRepository==deleteTask:$e'); 70 | } 71 | } 72 | int row = await taskDao.deleteTaskById(id); 73 | return row > 0; 74 | } 75 | 76 | Future modifyTaskStatus(Task task) async { 77 | if (loginProvider.isLogin()) { 78 | try { 79 | await api.modifyTaskStatus(task.id, task.status); 80 | } catch (e) { 81 | print('TaskRepository==modifyTaskStatus:$e'); 82 | } 83 | } 84 | taskDao.modifyTask(task); 85 | } 86 | 87 | Future updateTask(Task task) async { 88 | if (loginProvider.isLogin()) { 89 | try { 90 | await api.updateTask( 91 | id: task.id, 92 | title: task.title, 93 | content: task.content, 94 | date: task.dateStr, 95 | status: task.status, 96 | type: task.type, 97 | priority: task.priority); 98 | } catch (e) { 99 | print('TaskRepository==updateTask:$e'); 100 | } 101 | } 102 | await taskDao.modifyTask(task); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /lib/modules/task/task/task_controller.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: your name 3 | * @Date: 2020-12-08 20:57:12 4 | * @LastEditTime: 2020-12-12 15:33:25 5 | * @LastEditors: Please set LastEditors 6 | * @Description: In User Settings Edit 7 | * @FilePath: /todo/lib/modules/task/task/task_controller.dart 8 | */ 9 | import 'package:get/get.dart'; 10 | import 'package:pull_to_refresh/pull_to_refresh.dart'; 11 | import 'package:todo/data/db/task_database.dart'; 12 | import 'package:todo/data/model/task_model.dart'; 13 | import 'package:todo/data/providers/login_provider.dart'; 14 | import 'package:todo/data/repositories/task_repository.dart'; 15 | import 'package:todo/utils/extension/get_extension.dart'; 16 | 17 | class TaskController extends GetxController { 18 | final RefreshController refreshController = 19 | RefreshController(initialRefresh: true); 20 | int _pageNum = 1; 21 | List _tasks = new List(); 22 | List get tasks => _tasks; 23 | 24 | final TaskRepository _taskRepository = Get.find(); 25 | 26 | Future _load() async { 27 | try { 28 | TaskModel model = await _taskRepository.getTask(pageNum: _pageNum); 29 | return model; 30 | } catch (e) { 31 | print('_load' + e.toString()); 32 | return null; 33 | } 34 | } 35 | 36 | onRefresh() async { 37 | _pageNum = 1; 38 | TaskModel model = await _load(); 39 | if (model == null) { 40 | refreshController.refreshFailed(); 41 | return; 42 | } else { 43 | refreshController.refreshCompleted(); 44 | } 45 | if (model.over == true) { 46 | refreshController.loadNoData(); 47 | } 48 | 49 | if (model.datas?.isNotEmpty == true) { 50 | _tasks.clear(); 51 | _tasks.addAll(model.datas); 52 | _pageNum++; 53 | update(); 54 | } 55 | } 56 | 57 | onLoadMore() async { 58 | TaskModel model = await _load(); 59 | if (model == null) { 60 | refreshController.loadFailed(); 61 | return; 62 | } 63 | if (model.over == true) { 64 | refreshController.loadNoData(); 65 | } else { 66 | refreshController.loadComplete(); 67 | } 68 | 69 | if (model.datas?.isNotEmpty == true) { 70 | _tasks.addAll(model.datas); 71 | _pageNum++; 72 | update(); 73 | } 74 | } 75 | 76 | addNewTask(Task task) { 77 | _tasks.insert(0, task); 78 | update(); 79 | } 80 | 81 | deleteTask(int index) async { 82 | Get.loading(); 83 | try { 84 | bool success = await _taskRepository.deleteTask(_tasks[index].id); 85 | if (success) { 86 | _tasks.removeAt(index); 87 | update(); 88 | } 89 | } catch (e) { 90 | print('deleteTask' + e.toString()); 91 | } 92 | Get.dismiss(); 93 | 94 | // update(); 95 | } 96 | 97 | modifyTaskStatus(Task task) async { 98 | Get.loading(); 99 | try { 100 | await _taskRepository.modifyTaskStatus(task); 101 | var newTask = _tasks.firstWhere( 102 | (element) => element.id == task.id && element.status != task.status, 103 | orElse: () => null); 104 | if (newTask != null) { 105 | newTask.status = task.status; 106 | // print("modifyTaskStatus==${newTask.status}"); 107 | // int index = _tasks.indexOf(newTask); 108 | // print("modifyTaskStatus==${_tasks[index]}"); 109 | } 110 | update(); 111 | } catch (e) { 112 | print('modifyTaskStatus' + e.toString()); 113 | } 114 | Get.dismiss(); 115 | } 116 | 117 | @override 118 | void onReady() async { 119 | super.onReady(); 120 | LoginProvider loginProvider = Get.find(); 121 | print(loginProvider.isLogin()); 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /lib/modules/task/add/components/body.dart: -------------------------------------------------------------------------------- 1 | import 'package:get/get_state_manager/get_state_manager.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:get/get.dart'; 4 | import 'package:todo/data/model/priority.dart'; 5 | import '../add_task_controller.dart'; 6 | 7 | class Body extends GetView { 8 | @override 9 | Widget build(BuildContext context) { 10 | return SingleChildScrollView( 11 | child: Form( 12 | key: controller.formKey, 13 | child: Column( 14 | crossAxisAlignment: CrossAxisAlignment.start, 15 | children: [ 16 | SizedBox( 17 | height: 20, 18 | ), 19 | _buildTitleField( 20 | context, 21 | ), 22 | SizedBox( 23 | height: 20, 24 | ), 25 | Text( 26 | 'More:', 27 | style: Theme.of(context).textTheme.headline6, 28 | ), 29 | SizedBox( 30 | height: 20, 31 | ), 32 | _buildDescriptionField(context), 33 | SizedBox( 34 | height: 20, 35 | ), 36 | _buildDateTimeField(context), 37 | SizedBox( 38 | height: 20, 39 | ), 40 | _buildPriorityField(context), 41 | _buildSubmit(context), 42 | ], 43 | ), 44 | ), 45 | ); 46 | } 47 | 48 | /// title输入框 49 | Widget _buildTitleField(BuildContext context) { 50 | return TextFormField( 51 | style: Theme.of(context).textTheme.headline6.copyWith(fontSize: 18), 52 | validator: (value) => 53 | value.trim().isEmpty ? 'Please enter atask title' : null, 54 | onSaved: controller.saveTitle, 55 | decoration: InputDecoration( 56 | labelText: 'Title', 57 | labelStyle: TextStyle(fontSize: 18), 58 | border: OutlineInputBorder(borderRadius: BorderRadius.circular(10))), 59 | ); 60 | } 61 | 62 | /// Description输入框 63 | Widget _buildDescriptionField(BuildContext context) { 64 | return TextFormField( 65 | style: Theme.of(context).textTheme.headline6.copyWith(fontSize: 18), 66 | onSaved: controller.saveContent, 67 | maxLines: 6, 68 | decoration: InputDecoration( 69 | labelText: 'Description', 70 | hintText: 'Add description here', 71 | border: OutlineInputBorder( 72 | borderRadius: BorderRadius.only( 73 | topLeft: Radius.circular(15), 74 | topRight: Radius.circular(15)))), 75 | ); 76 | } 77 | 78 | /// 时间选择器 79 | Widget _buildDateTimeField(BuildContext context) { 80 | return GetBuilder( 81 | init: controller, 82 | id: controller.updateDateId, 83 | builder: (_) { 84 | print(" _.dateTimeControlle==${_.dateTimeController}"); 85 | return TextFormField( 86 | readOnly: true, 87 | onTap: _.handleDatePicker, 88 | controller: _.dateTimeController, 89 | style: Theme.of(context).textTheme.headline6.copyWith(fontSize: 18), 90 | decoration: InputDecoration( 91 | labelText: 'DateTime', 92 | labelStyle: TextStyle(fontSize: 18), 93 | border: 94 | OutlineInputBorder(borderRadius: BorderRadius.circular(10))), 95 | ); 96 | }, 97 | ); 98 | } 99 | 100 | /// 优先级输入框 101 | Widget _buildPriorityField(BuildContext context) { 102 | return GetBuilder( 103 | init: controller, 104 | id: controller.updatePriorityId, 105 | builder: (_) { 106 | return DropdownButtonFormField( 107 | isDense: true, 108 | icon: Icon(Icons.arrow_drop_down_circle), 109 | iconSize: 22, 110 | iconEnabledColor: Theme.of(context).primaryColor, 111 | style: Theme.of(context).textTheme.subtitle1, 112 | decoration: InputDecoration( 113 | labelText: 'Priority', 114 | labelStyle: TextStyle(fontSize: 18), 115 | border: 116 | OutlineInputBorder(borderRadius: BorderRadius.circular(10))), 117 | items: prioritiesStr.map((e) { 118 | return DropdownMenuItem( 119 | value: e, 120 | child: Text(e, 121 | style: Theme.of(context).textTheme.subtitle1.copyWith( 122 | color: priorityColor[prioritiesStr.indexOf(e)])), 123 | ); 124 | }).toList(), 125 | value: prioritiesStr[0], 126 | onChanged: _.changePriority, 127 | ); 128 | }, 129 | ); 130 | } 131 | 132 | Widget _buildSubmit(BuildContext context) { 133 | return Container( 134 | margin: EdgeInsets.symmetric(vertical: 30), 135 | height: 60, 136 | width: double.infinity, 137 | decoration: BoxDecoration( 138 | color: Theme.of(context).primaryColor, 139 | borderRadius: BorderRadius.circular(30)), 140 | child: FlatButton( 141 | onPressed: controller.submit, 142 | child: Text('add task', 143 | style: Theme.of(context) 144 | .textTheme 145 | .button 146 | .copyWith(color: Colors.white)), 147 | ), 148 | ); 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /lib/modules/task/edit/components/body.dart: -------------------------------------------------------------------------------- 1 | import 'package:get/get_state_manager/get_state_manager.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:get/get.dart'; 4 | import 'package:todo/data/model/priority.dart'; 5 | import '../edit_task_controller.dart'; 6 | 7 | class Body extends GetView { 8 | @override 9 | Widget build(BuildContext context) { 10 | return SingleChildScrollView( 11 | child: Form( 12 | key: controller.formKey, 13 | child: Column( 14 | crossAxisAlignment: CrossAxisAlignment.start, 15 | children: [ 16 | SizedBox( 17 | height: 20, 18 | ), 19 | _buildTitleField( 20 | context, 21 | ), 22 | SizedBox( 23 | height: 20, 24 | ), 25 | Text( 26 | 'More:', 27 | style: Theme.of(context).textTheme.headline6, 28 | ), 29 | SizedBox( 30 | height: 20, 31 | ), 32 | _buildDescriptionField(context), 33 | SizedBox( 34 | height: 20, 35 | ), 36 | _buildDateTimeField(context), 37 | SizedBox( 38 | height: 20, 39 | ), 40 | _buildPriorityField(context), 41 | _buildSubmit(context), 42 | ], 43 | ), 44 | ), 45 | ); 46 | } 47 | 48 | /// title输入框 49 | Widget _buildTitleField(BuildContext context) { 50 | return TextFormField( 51 | initialValue: controller.task.title, 52 | style: Theme.of(context).textTheme.headline6.copyWith(fontSize: 18), 53 | validator: (value) => 54 | value.trim().isEmpty ? 'Please enter atask title' : null, 55 | onSaved: controller.saveTitle, 56 | decoration: InputDecoration( 57 | labelText: 'Title', 58 | labelStyle: TextStyle(fontSize: 18), 59 | border: OutlineInputBorder(borderRadius: BorderRadius.circular(10))), 60 | ); 61 | } 62 | 63 | /// Description输入框 64 | Widget _buildDescriptionField(BuildContext context) { 65 | return TextFormField( 66 | style: Theme.of(context).textTheme.headline6.copyWith(fontSize: 18), 67 | onSaved: controller.saveContent, 68 | maxLines: 6, 69 | initialValue: controller.task.content, 70 | decoration: InputDecoration( 71 | labelText: 'Description', 72 | hintText: 'Add description here', 73 | border: OutlineInputBorder( 74 | borderRadius: BorderRadius.only( 75 | topLeft: Radius.circular(15), 76 | topRight: Radius.circular(15)))), 77 | ); 78 | } 79 | 80 | /// 时间选择器 81 | Widget _buildDateTimeField(BuildContext context) { 82 | return GetBuilder( 83 | init: controller, 84 | id: controller.updateDateId, 85 | builder: (_) { 86 | return TextFormField( 87 | readOnly: true, 88 | onTap: _.handleDatePicker, 89 | controller: _.dateTimeController, 90 | style: Theme.of(context).textTheme.headline6.copyWith(fontSize: 18), 91 | decoration: InputDecoration( 92 | labelText: 'DateTime', 93 | labelStyle: TextStyle(fontSize: 18), 94 | border: 95 | OutlineInputBorder(borderRadius: BorderRadius.circular(10))), 96 | ); 97 | }, 98 | ); 99 | } 100 | 101 | /// 优先级输入框 102 | Widget _buildPriorityField(BuildContext context) { 103 | return GetBuilder( 104 | init: controller, 105 | id: controller.updatePriorityId, 106 | builder: (_) { 107 | return DropdownButtonFormField( 108 | isDense: true, 109 | icon: Icon(Icons.arrow_drop_down_circle), 110 | iconSize: 22, 111 | iconEnabledColor: Theme.of(context).primaryColor, 112 | style: Theme.of(context).textTheme.subtitle1, 113 | decoration: InputDecoration( 114 | labelText: 'Priority', 115 | labelStyle: TextStyle(fontSize: 18), 116 | border: 117 | OutlineInputBorder(borderRadius: BorderRadius.circular(10))), 118 | items: prioritiesStr.map((e) { 119 | return DropdownMenuItem( 120 | value: e, 121 | child: Text(e, 122 | style: Theme.of(context).textTheme.subtitle1.copyWith( 123 | color: priorityColor[prioritiesStr.indexOf(e)])), 124 | ); 125 | }).toList(), 126 | value: prioritiesStr[controller.task.priority], 127 | onChanged: _.changePriority, 128 | ); 129 | }, 130 | ); 131 | } 132 | 133 | Widget _buildSubmit(BuildContext context) { 134 | return Container( 135 | margin: EdgeInsets.symmetric(vertical: 30), 136 | height: 60, 137 | width: double.infinity, 138 | decoration: BoxDecoration( 139 | color: Theme.of(context).primaryColor, 140 | borderRadius: BorderRadius.circular(30)), 141 | child: FlatButton( 142 | onPressed: controller.submit, 143 | child: Text('update', 144 | style: Theme.of(context) 145 | .textTheme 146 | .button 147 | .copyWith(color: Colors.white)), 148 | ), 149 | ); 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /lib/modules/task/details/task_details.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: your name 3 | * @Date: 2020-12-09 20:53:52 4 | * @LastEditTime: 2020-12-12 13:52:05 5 | * @LastEditors: Please set LastEditors 6 | * @Description: In User Settings Edit 7 | * @FilePath: /todo/lib/modules/task/details/task_details.dart 8 | */ 9 | /* 10 | * @Author: your name 11 | * @Date: 2020-12-09 20:53:52 12 | * @LastEditTime: 2020-12-09 22:11:35 13 | * @LastEditors: Please set LastEditors 14 | * @Description: In User Settings Edit 15 | * @FilePath: /todo/lib/modules/task/details/task_details.dart 16 | */ 17 | import 'package:flutter/material.dart'; 18 | import 'package:get/get.dart'; 19 | import 'package:todo/data/db/task_database.dart'; 20 | import 'package:todo/data/model/priority.dart'; 21 | 22 | class TaskDetailsPage extends StatelessWidget { 23 | final Color color1 = Color(0xFFc44dff); 24 | final Color color2 = Color(0xFFD693F1); 25 | final Color color3 = Color(0xFFA23ED4); 26 | 27 | @override 28 | Widget build(BuildContext context) { 29 | Task task = Get.arguments; 30 | return Scaffold( 31 | appBar: AppBar(title: Text('TaskDetailsPage')), 32 | body: SingleChildScrollView( 33 | child: Column( 34 | crossAxisAlignment: CrossAxisAlignment.start, 35 | children: [ 36 | _buildHeader(context), 37 | _buildKey(context, 'Title:'), 38 | _buildValue(context, task.title), 39 | _buildKey(context, 'Description:'), 40 | _buildValue(context, task.content), 41 | _buildKey(context, 'Date:'), 42 | _buildValue(context, task.dateStr), 43 | _buildKey(context, 'Priority:'), 44 | _buildValue(context, prioritiesStr[task.priority]), 45 | _buildKey(context, '进度:'), 46 | _buildValue(context, task.status == 0 ? '未完成' : '已完成'), 47 | ], 48 | ), 49 | )); 50 | } 51 | 52 | Widget _buildValue( 53 | BuildContext context, 54 | String value, 55 | ) { 56 | return Container( 57 | width: double.infinity, 58 | margin: EdgeInsets.all(16), 59 | padding: EdgeInsets.all(8), 60 | decoration: BoxDecoration( 61 | border: Border.all( 62 | color: Theme.of(context).primaryColor, width: 1), // 边色与边宽度 63 | // borderRadius: new BorderRadius.circular((20.0)), // 圆角度 64 | borderRadius: BorderRadius.all(Radius.circular(8)), 65 | ), 66 | child: Text(value ?? '', style: Theme.of(context).textTheme.subtitle1)); 67 | } 68 | 69 | Widget _buildKey( 70 | BuildContext context, 71 | String value, 72 | ) { 73 | return Container( 74 | margin: EdgeInsets.only(left: 20, top: 0), 75 | child: Text(value, 76 | style: Theme.of(context) 77 | .textTheme 78 | .headline5 79 | .copyWith(color: Colors.black))); 80 | } 81 | 82 | Widget _buildHeader(BuildContext context) { 83 | return Container( 84 | height: 250, 85 | width: double.infinity, 86 | child: Stack( 87 | children: [ 88 | Positioned( 89 | top: -150, 90 | left: -100, 91 | bottom: 0, 92 | child: Container( 93 | width: 350, 94 | decoration: BoxDecoration( 95 | gradient: LinearGradient(colors: [color3, color1]), 96 | shape: BoxShape.circle, 97 | boxShadow: [ 98 | BoxShadow( 99 | color: color2, 100 | offset: Offset(4, 4), 101 | blurRadius: 10, 102 | ) 103 | ]), 104 | ), 105 | ), 106 | Container( 107 | width: 100, 108 | height: 100, 109 | decoration: BoxDecoration( 110 | shape: BoxShape.circle, 111 | gradient: LinearGradient(colors: [color3, color2]), 112 | boxShadow: [ 113 | BoxShadow( 114 | color: color2, offset: Offset(1, 1), blurRadius: 4) 115 | ])), 116 | Positioned( 117 | top: 100, 118 | right: 220, 119 | child: Container( 120 | width: 50, 121 | height: 50, 122 | decoration: BoxDecoration( 123 | shape: BoxShape.circle, 124 | gradient: LinearGradient(colors: [color3, color2]), 125 | boxShadow: [ 126 | BoxShadow( 127 | color: color2, offset: Offset(1, 1), blurRadius: 4) 128 | ])), 129 | ), 130 | Container( 131 | margin: EdgeInsets.only(top: 60, left: 50), 132 | child: Column( 133 | crossAxisAlignment: CrossAxisAlignment.start, 134 | children: [ 135 | Text('Hi Man', 136 | style: Theme.of(context).textTheme.headline5.copyWith( 137 | color: Colors.white, fontWeight: FontWeight.bold)), 138 | SizedBox( 139 | height: 10, 140 | ), 141 | Text('get it today!', 142 | style: Theme.of(context).textTheme.subtitle1.copyWith( 143 | color: Colors.white, 144 | )), 145 | ], 146 | )) 147 | ], 148 | )); 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /lib/modules/profile/profile_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:todo/r.dart'; 3 | 4 | class ProfilePage extends StatelessWidget { 5 | final List imgs = [ 6 | R.assetsImagesGetx1, 7 | R.assetsImagesGetx2, 8 | R.assetsImagesGetx3, 9 | R.assetsImagesGetx4 10 | ]; 11 | @override 12 | Widget build(BuildContext context) { 13 | Size size = MediaQuery.of(context).size; 14 | 15 | return Scaffold( 16 | extendBodyBehindAppBar: true, 17 | appBar: AppBar( 18 | backgroundColor: Colors.transparent, 19 | elevation: 0, 20 | ), 21 | body: Stack( 22 | children: [ 23 | Center( 24 | child: Image.asset( 25 | R.assetsImagesProfile, 26 | fit: BoxFit.cover, 27 | width: size.width, 28 | height: size.height, 29 | ), 30 | ), 31 | Padding( 32 | padding: EdgeInsets.symmetric( 33 | horizontal: 8, 34 | vertical: 16, 35 | ), 36 | child: Align( 37 | alignment: Alignment.bottomCenter, 38 | child: Card( 39 | margin: EdgeInsets.all(0), 40 | clipBehavior: Clip.antiAlias, 41 | shape: RoundedRectangleBorder( 42 | borderRadius: BorderRadius.circular(10), 43 | ), 44 | elevation: 4, 45 | child: Container( 46 | height: size.height * 0.45, 47 | padding: EdgeInsets.symmetric(vertical: 16, horizontal: 8), 48 | child: Padding( 49 | padding: EdgeInsets.symmetric( 50 | horizontal: 16, 51 | ), 52 | child: Column( 53 | crossAxisAlignment: CrossAxisAlignment.start, 54 | children: [ 55 | Row( 56 | mainAxisAlignment: MainAxisAlignment.start, 57 | children: [ 58 | Container( 59 | decoration: BoxDecoration( 60 | borderRadius: BorderRadius.all( 61 | Radius.circular(20), 62 | ), 63 | border: Border.all( 64 | color: Colors.grey, 65 | ), 66 | ), 67 | padding: EdgeInsets.symmetric( 68 | vertical: 8, 69 | horizontal: 12, 70 | ), 71 | child: Center( 72 | child: Text( 73 | "ANDROID", 74 | style: TextStyle( 75 | fontSize: 14, 76 | ), 77 | ), 78 | ), 79 | ), 80 | SizedBox( 81 | width: 8, 82 | ), 83 | Container( 84 | decoration: BoxDecoration( 85 | borderRadius: BorderRadius.all( 86 | Radius.circular(20), 87 | ), 88 | border: Border.all( 89 | color: Colors.grey, 90 | ), 91 | ), 92 | padding: EdgeInsets.symmetric( 93 | vertical: 8, 94 | horizontal: 12, 95 | ), 96 | child: Center( 97 | child: Text( 98 | "WEB", 99 | style: TextStyle( 100 | fontSize: 14, 101 | ), 102 | ), 103 | ), 104 | ), 105 | SizedBox( 106 | width: 8, 107 | ), 108 | Container( 109 | decoration: BoxDecoration( 110 | color: Theme.of(context).primaryColor, 111 | borderRadius: BorderRadius.all( 112 | Radius.circular(20), 113 | ), 114 | ), 115 | padding: EdgeInsets.symmetric( 116 | vertical: 8, 117 | horizontal: 12, 118 | ), 119 | child: Center( 120 | child: Text( 121 | "FLUTTER", 122 | style: TextStyle( 123 | fontSize: 14, 124 | color: Colors.white, 125 | ), 126 | ), 127 | ), 128 | ), 129 | ], 130 | ), 131 | SizedBox( 132 | height: 8, 133 | ), 134 | Text( 135 | "Asi", 136 | style: TextStyle( 137 | fontSize: 32, 138 | fontWeight: FontWeight.bold, 139 | ), 140 | ), 141 | Text( 142 | "Programmer", 143 | style: TextStyle( 144 | fontSize: 14, 145 | color: Colors.grey, 146 | ), 147 | ), 148 | SizedBox( 149 | height: 8, 150 | ), 151 | Text( 152 | "I am a developer Advocate for material-components at hangzhou", 153 | style: TextStyle( 154 | fontSize: 14, 155 | ), 156 | ), 157 | Text( 158 | "GetX", 159 | style: TextStyle( 160 | color: Colors.black87, 161 | fontSize: 16, 162 | fontWeight: FontWeight.bold, 163 | ), 164 | ), 165 | Container( 166 | height: 160, 167 | child: ListView.builder( 168 | padding: EdgeInsets.all(8), 169 | physics: BouncingScrollPhysics(), 170 | scrollDirection: Axis.horizontal, 171 | itemCount: imgs.length, 172 | itemBuilder: (BuildContext context, int index) { 173 | return Container( 174 | height: 160, 175 | width: 110, 176 | margin: EdgeInsets.symmetric(horizontal: 4), 177 | decoration: BoxDecoration( 178 | borderRadius: BorderRadius.circular(10), 179 | image: DecorationImage( 180 | fit: BoxFit.cover, 181 | image: AssetImage(imgs[index]), 182 | ))); 183 | }), 184 | ) 185 | ], 186 | ), 187 | ), 188 | ), 189 | ), 190 | ), 191 | ), 192 | ], 193 | ), 194 | ); 195 | } 196 | } 197 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 在使用了 Provider 一年后,遇到了很多阻力,期间尝试过 BLoC 、MobX ,均不如意,一个样本代码太多,使用复杂,一个生产代码要等很久。难道 Flutter 就没有诸如原生 Android 的 jetpack 套装一样方便的套件吗?后来开始尝试 GetX,才发现真香,正如作者所说: 2 | 3 | > GetX是Flutter的超轻便且强大的解决方案。它以快速实用的方式结合了高性能状态管理,智能依赖性注入和路由管理。 4 | 5 | 我写了一个[demo](https://www.jianshu.com/p/920db9e968f0)探索过了基本使用方式之后,又决定写一个 待办清单app 实践一下 Clean Architecture 。 6 | 7 | 首先感谢下鸿洋大佬的 [todo api](https://www.wanandroid.com/blog/show/2442),第一版是利用 api 开发的一个在线应用,后来在不注册的情况下,加入 moor 数据库,可以离线使用。这一部分改的仓促,下一期迭代会改进。 8 | 9 | ## 项目依赖和结构 10 | 11 | ``` 12 | dependencies: 13 | flutter: 14 | sdk: flutter 15 | cookie_jar: ^1.0.1 16 | cupertino_icons: ^1.0.0 17 | date_format: ^1.0.9 18 | dio: ^3.0.10 19 | dio_cookie_manager: ^1.0.0 20 | dio_http_cache: ^0.2.11 21 | flutter_slidable: ^0.5.7 22 | get: ^3.21.2 23 | google_fonts: ^1.1.1 24 | moor: ^3.4.0 25 | path: ^1.7.0 26 | path_provider: ^1.6.24 27 | pull_to_refresh: ^1.6.3 28 | shared_preferences: ^0.5.12+4 29 | table_calendar: ^2.3.1 30 | ``` 31 | 32 | 项目网络模块封装了 dio,因为是 带 cookie 的 请求,所以加入了 cookie 和本地化,算是一个比较完善的请求模块。 33 | 34 | 数据库选用了 moor ,Android 中 room 的字母倒过来就是这个,和 room 一样可以响应式,十分优秀。 35 | 36 | 剩下的第三方包就是分页和侧滑控件,还有一个日历包。 37 | 38 | 整体项目的结构参考[getx_pattern](https://kauemurakami.github.io/getx_pattern/),又按照自己的习惯做了修改。 39 | ![getx_pattern](https://kauemurakami.github.io/getx_pattern/images/strc.png) 40 | 41 | ## 从 GetX 开始开发 42 | 43 | ### 使用 GetX 44 | ``` 45 | void main() async { 46 | runApp(GetMaterialApp( 47 | debugShowCheckedModeBanner: false, 48 | initialRoute: '/', 49 | builder: (context, child) => Scaffold( 50 | // Global GestureDetector that will dismiss the keyboard 51 | body: GestureDetector( 52 | onTap: () { 53 | hideKeyboard(context); 54 | }, 55 | child: child, 56 | ), 57 | ), 58 | theme: appThemeData, 59 | defaultTransition: Transition.fade, 60 | getPages: AppPages.pages, 61 | initialBinding: SplashBinding(), 62 | home: SplashPage(), 63 | )); 64 | } 65 | ``` 66 | 67 | ### 命名路由 68 | 69 | 要使用完整的路由功能,需要把 MaterialApp 替换为 GetMaterialApp ,中间加入的`builder` 是为了解决点击空白处隐藏键盘的需求,这个在原生也很常见。 70 | 71 | 72 | ```abstract class AppPages { 73 | static final pages = [ 74 | GetPage( 75 | name: Routes.LOGIN, 76 | page: () => LoginPage(), 77 | binding: LoginPageBinding(), 78 | ), 79 | GetPage( 80 | name: Routes.SPLASH, 81 | page: () => SplashPage(), 82 | binding: SplashBinding(), 83 | ), 84 | GetPage( 85 | name: Routes.SIGN_UP, 86 | page: () => SignUpPage(), 87 | binding: SiginUpBinding(), 88 | ), 89 | GetPage( 90 | name: Routes.TASK, 91 | page: () => TaskPage(), 92 | binding: TaskBinding(), 93 | ), 94 | GetPage( 95 | name: Routes.TASK_ADD, 96 | page: () => AddTaskPage(), 97 | binding: AddTaskBinding(), 98 | ), 99 | GetPage( 100 | name: Routes.TASK_DETAILS, 101 | page: () => TaskDetailsPage(), 102 | ), 103 | GetPage( 104 | name: Routes.TASK_EDIT, 105 | page: () => EditTaskPage(), 106 | binding: EditTaskBinding(), 107 | ), 108 | GetPage( 109 | name: Routes.TASK_MOTHLY, 110 | page: () => MonthlyPage(), 111 | binding: MonthlyBinding(), 112 | ), 113 | GetPage( 114 | name: Routes.PROFILE, 115 | page: () => ProfilePage(), 116 | ), 117 | ]; 118 | } 119 | ``` 120 | 121 | 习惯了使用命名路由,所以定义了路由表。`binding`是 GetX 中我特别喜欢的功能——依赖注入,就像原生的 Hilt 一样,让代码结构无侵分层。并且如果使用的是流或计时器,它们将自动关闭,开发者根据不用担心。Binding 类是一个解耦依赖注入的类,在路由的时候使用。就可以知道注入的作用域,以及知道在何处以及如何处置注入的对象。 122 | 123 | ### 登录 124 | 125 | api 是玩安卓的开放 api,登录要使用 api 和 repository,所以依赖注入的形式注入: 126 | 127 | ``` 128 | class LoginPageBinding implements Bindings { 129 | @override 130 | void dependencies() { 131 | Get.lazyPut(() => LoginApi()); 132 | Get.lazyPut(() => LoginRepository()); 133 | Get.lazyPut( 134 | () => LoginController(), 135 | ); 136 | } 137 | } 138 | ``` 139 | 在使用的时候直接 `find`: 140 | 141 | ```class LoginController extends GetxController { 142 | final LoginRepository repository = Get.find(); 143 | ``` 144 | `Get.put()`是最常见的注入依赖的方法,它是直接注入到内存里。你可以在任何地方找到注入的对象,这是 Provider 所没有的功能。 145 | 146 | 仅有`put`还不够,GetX 还提供另外一个方法,`Get.lazyPut`可以懒加载一个依赖,这样它只有在使用时才会被实例化。这对于计算代价高的类来说非常有用,或者如果你想在一个地方实例化几个类(比如在 Bindings 类中),但是不知道会不会使用到,那懒加载是正确的选择,是不是很像 kotlin 的 lazy。 147 | 148 | 显示密码的功能暂时未加。 149 | ![登录](https://upload-images.jianshu.io/upload_images/1454742-fb3359da0f42fd30.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 150 | 151 | 152 | 在欢迎页会注入全局的依赖,然后判断是否登录,对应不同的导航: 153 | 154 | ```class SplashController extends GetxController { 155 | @override 156 | void onReady() async { 157 | super.onReady(); 158 | await GloabConfig.init(); 159 | await DenpendencyInjection.init(); 160 | LoginProvider loginProvider = Get.find(); 161 | print(loginProvider); 162 | // 如果未登录就登录 163 | // 如果已登录就去task页面 164 | if (loginProvider.isLogin()) { 165 | Get.offNamed(Routes.TASK); 166 | } else { 167 | Get.offNamed(Routes.LOGIN); 168 | } 169 | } 170 | } 171 | ``` 172 | ### Task 列表 173 | ![task](https://upload-images.jianshu.io/upload_images/1454742-0747afc8dba30f00.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 174 | 175 | 176 | 主页实现了底部导航和嵌入式`FloatingActionButtonLocation`,没有任务的时候会弹出使用引导。点击加号可以添加任务。因为 api 是分页的,所以也做了分页处理。 177 | 178 | 179 | ```class TaskPage extends GetView { 180 | @override 181 | Widget build(BuildContext context) { 182 | return Scaffold( 183 | appBar: AppBar(title: Text('My Task')), 184 | body: Body(), 185 | floatingActionButton: FloatingActionButton( 186 | onPressed: () { 187 | Get.toNamed(Routes.TASK_ADD); 188 | }, 189 | child: Icon(Icons.add), 190 | ), 191 | floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked, 192 | bottomNavigationBar: BottomAppBar( 193 | shape: CircularNotchedRectangle(), 194 | child: Row( 195 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 196 | children: [ 197 | IconButton( 198 | icon: Icon(Icons.calendar_today_sharp), 199 | onPressed: () { 200 | Get.toNamed(Routes.TASK_MOTHLY); 201 | }, 202 | ), 203 | IconButton( 204 | icon: Icon(Icons.settings), 205 | onPressed: () { 206 | Get.toNamed(Routes.PROFILE); 207 | }, 208 | ), 209 | ], 210 | ), 211 | ), 212 | ); 213 | } 214 | } 215 | ``` 216 | 任务 `item`可以点击进入详情和侧滑,有两个侧滑菜单,编辑和删除,对应不同的功能,圆形的`checkbox`可以完成任务,任务标题和时间在完成时会有删除线。 217 | 218 | `GetView` 就是封装的`StatelessWidget`,内部有一个 `get`方法便捷的获取注入的`controller`,这样连获取的步骤都能省略。 219 | 220 | ### 增加和编辑 221 | ![编辑](https://upload-images.jianshu.io/upload_images/1454742-89b538cf18fbfba7.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 222 | 223 | ![添加](https://upload-images.jianshu.io/upload_images/1454742-6e1eda3d5c4dd8a5.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 224 | 225 | 对应的标题是必须项,描述可以为空,时间是默认当前,优先级有高低中三个,默认是中。 226 | 227 | 选择日期会弹出日历你,采用局部刷新,提高性能,`update([updateDateId])`函数的参数是一个 id,只会刷新对应 id 的 `GetBuilder`,并且 GetX 不受 `InheritedWidget`的限制,所以可以在任意地方引用未被内存回收的 `Controller`,所以可以在编辑页面,让列表页也同时刷新。 228 | ![日历](https://upload-images.jianshu.io/upload_images/1454742-d46509215cbf3120.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 229 | 230 | ``` 231 | void handleDatePicker() async { 232 | final datePick = await showDatePicker( 233 | context: Get.context, 234 | firstDate: DateTime(2000), 235 | initialDate: _dateTime, 236 | lastDate: DateTime(2100)); 237 | if (datePick != null && datePick != _dateTime) { 238 | _dateTime = datePick; 239 | task.dateStr = _dateTime.format(); 240 | dateTimeController.text = task.dateStr; 241 | update([updateDateId]); 242 | } 243 | } 244 | ``` 245 | 246 | 247 | ``` 248 | void submit() async { 249 | if (formKey.currentState.validate()) { 250 | formKey.currentState.save(); 251 | try { 252 | Get.loading(); 253 | await _taskRepository.updateTask(task); 254 | Get.dismiss(); 255 | // 刷新列表页 256 | Get.find().update(); 257 | // controller.updateTask(task); 258 | Get.back(); 259 | } catch (e) { 260 | print(e); 261 | Get.dismiss(); 262 | Get.snackbar('Error', e.toString()); 263 | } 264 | } 265 | } 266 | ``` 267 | 268 | ### 月份视图 269 | 270 | ![月份视图](https://upload-images.jianshu.io/upload_images/1454742-7c31f3df721e97a6.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 271 | 272 | 273 | 月份视图用了`table_calendar`包,这个包功能强大,可以定制日历视图。默认显示两周,点击月份展开四周的月份视图。可以按日期筛选出任务。这里的任务可以点击进入详情和点击`checkbox`更改状态。 274 | 275 | ``` 276 | TableCalendar( 277 | onDaySelected: (DateTime day, _, __) { 278 | controller.selectedDate(day); 279 | }, 280 | calendarController: controller.calendarController, 281 | startingDayOfWeek: StartingDayOfWeek.monday, 282 | initialCalendarFormat: CalendarFormat.week, 283 | calendarStyle: CalendarStyle( 284 | selectedColor: Theme.of(context).accentColor, 285 | ), 286 | ) 287 | ``` 288 | 289 | 这里更改状态后,同样可以拿到列表页的`Controller`去更新列表页: 290 | 291 | ``` 292 | modifyTaskStatus(Task task) async { 293 | try { 294 | TaskController taskController = Get.find(); 295 | await taskController.modifyTaskStatus(task); 296 | } catch (e) {} 297 | update(); 298 | } 299 | ``` 300 | 301 | ### 个人中心 302 | 303 | ![个人中心](https://upload-images.jianshu.io/upload_images/1454742-6a4bb885af61cbe3.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 304 | 305 | 306 | 个人中心是一个静态页面,最下面展示了我写的 GetX 的 demo 截图。点击放大的功能放在迭代里做吧。 307 | 308 | 这里藏有福利,一个漂亮的二次元萌妹子。 309 | 310 | ### 扩展函数 311 | 在 `utils`文件夹下写了两个扩展函数,扩展了日期格式化和基于 GetX 的全局加载框。 312 | 313 | ``` 314 | extension DateExtension on DateTime { 315 | String format() { 316 | return formatDate(this, [ 317 | yyyy, 318 | '-', 319 | mm, 320 | '-', 321 | dd, 322 | ]); 323 | } 324 | } 325 | 326 | ``` 327 | 328 | ``` 329 | extension GetExtension on GetInterface { 330 | dismiss() { 331 | if (Get.isDialogOpen) { 332 | Get.back(); 333 | } 334 | } 335 | 336 | loading() { 337 | if (Get.isDialogOpen) { 338 | Get.back(); 339 | } 340 | Get.dialog(LoadingDialog()); 341 | } 342 | } 343 | ``` 344 | 345 | 346 | 使用也很简单,但不要忘了要导入扩展函数类: 347 | 348 | ``` 349 | dateTime.format(); 350 | ``` 351 | 352 | 353 | ``` 354 | Get.loading(); 355 | 。。。。。。 356 | Get.dismiss(); 357 | ``` 358 | 359 | ### GetService 360 | 361 | GetService 我的理解是类似服务,比如 SharedPreferences、Database,还有需要异步初始化的类,放在这里注入非常合适: 362 | 363 | ```class TaskDaoController extends GetxService { 364 | TaskDao init() { 365 | TaskDatabase database = TaskDatabase(); 366 | return TaskDao(database); 367 | } 368 | } 369 | ``` 370 | 371 | ``` 372 | class AppSpController extends GetxService { 373 | Future init() async { 374 | return await SharedPreferences.getInstance(); 375 | } 376 | } 377 | 378 | ``` 379 | 380 | 同步的就用同步方法注入: 381 | 382 | ``` 383 | // 数据库 384 | Get.put(TaskDaoController().init()); 385 | ``` 386 | 异步的用异步方法注入: 387 | 388 | ``` 389 | // shared_preferences 390 | await Get.putAsync(() => AppSpController().init()); 391 | ``` 392 | 393 | ### 数据库 moor 的使用 394 | Android 通过 room 给开发带来的便利,用过的都知道。moor 就是 Flutter 上的 room。 395 | 396 | Moor 使用 Dart 的源代码生成器生成代码,我们可以用函数式的调用操作数据库。这也是需要 moor_generator 依赖项以及 build_runner 的原因。 397 | 398 | moor 优点之一是我们可以完全使用 Dart 操作数据库,而不必写数据库语句。这也适用于定义SQL表。创建一个表示 table 的类即可。 399 | 400 | 401 | ``` 402 | class Tasks extends Table { 403 | // 可空类型 404 | IntColumn get completeDate => integer().nullable()(); 405 | TextColumn get completeDateStr => text().nullable()(); 406 | TextColumn get content => text().nullable()(); 407 | 408 | // 为空自动生成默认值 409 | IntColumn get date => 410 | integer().clientDefault(() => DateTime.now().millisecondsSinceEpoch)(); 411 | 412 | // 为空自动生成默认值 413 | TextColumn get dateStr => 414 | text().nullable().clientDefault(() => DateTime.now().format())(); 415 | 416 | // 主键 417 | IntColumn get id => integer().nullable().autoIncrement()(); 418 | 419 | // 为空自动生成默认值 420 | IntColumn get priority => integer().nullable().withDefault(Constant(0))(); 421 | 422 | // 为空自动生成默认值 423 | IntColumn get status => integer().nullable().withDefault(Constant(0))(); 424 | 425 | TextColumn get title => text()(); 426 | 427 | IntColumn get type => integer().withDefault(Constant(0))(); 428 | 429 | IntColumn get userId => integer().nullable()(); 430 | } 431 | 432 | @UseMoor(tables: [Tasks], daos: [TaskDao]) 433 | class TaskDatabase extends _$TaskDatabase { 434 | // we tell the database where to store the data with this constructor 435 | TaskDatabase() : super(_openConnection()); 436 | 437 | // you should bump this number whenever you change or add a table definition. Migrations 438 | // are covered later in this readme. 439 | @override 440 | int get schemaVersion => 1; 441 | } 442 | 443 | LazyDatabase _openConnection() { 444 | // the LazyDatabase util lets us find the right location for the file async. 445 | return LazyDatabase(() async { 446 | // put the database file, called db.sqlite here, into the documents folder 447 | // for your app. 448 | final dbFolder = await getApplicationDocumentsDirectory(); 449 | final file = File(join(dbFolder.path, 'db.sqlite')); 450 | return VmDatabase(file); 451 | }); 452 | } 453 | ``` 454 | 数据库操作写在这里也可以,但是会显得臃肿,moor 还提供 Dao ,把操作放在 Dao 类是个好习惯: 455 | 456 | ``` 457 | 458 | @UseDao(tables: [Tasks]) 459 | class TaskDao extends DatabaseAccessor with _$TaskDaoMixin { 460 | TaskDao(TaskDatabase db) : super(db); 461 | 462 | /// 获取全部 463 | Future> get getAllTasks => select(tasks).get(); 464 | 465 | ///imit查询来限制返回的结果数量 466 | ///offset偏移量 467 | Future> getTasks(int limit, {int offset}) { 468 | return (select(tasks)..limit(limit, offset: offset)).get(); 469 | } 470 | 471 | ///imit查询来限制返回的结果数量 472 | ///offset偏移量 473 | Future> getTasksWithDateStr(String dateStr) { 474 | return (select(tasks)..where((e) => e.dateStr.equals(dateStr))).get(); 475 | } 476 | 477 | /// 获取单个数据 478 | /// 没必要用list 479 | Future getTaskById(int id) { 480 | return (select(tasks)..where((t) => t.id.equals(id))).getSingle(); 481 | } 482 | 483 | Future updateTask(Task entry) { 484 | TasksCompanion(); 485 | 486 | return update(tasks).replace(entry); 487 | } 488 | 489 | Future createOrUpdateUser(String title, 490 | {String content, String date, int type = 0, int priority = 0}) { 491 | return into(tasks).insertOnConflictUpdate(TasksCompanion( 492 | title: Value(title), 493 | content: Value(content), 494 | dateStr: Value(date), 495 | type: Value(type), 496 | priority: Value(priority), 497 | )); 498 | } 499 | 500 | Future createTask(TasksCompanion task) async { 501 | var id = await into(tasks).insertOnConflictUpdate(task); 502 | return getTaskById(id); 503 | } 504 | 505 | /// 批量插入 506 | Future insertMultipleTasks(List entries) async { 507 | await batch((batch) { 508 | batch.insertAll(tasks, entries); 509 | }); 510 | } 511 | 512 | Future deleteTaskById(int id) { 513 | return (delete(tasks)..where((t) => t.id.equals(id))).go(); 514 | } 515 | 516 | Future deleteTask(Task entry) { 517 | return delete(tasks).delete(entry); 518 | } 519 | 520 | Future modifyStatusByid(int id, int status) async { 521 | // into(tasks).up 522 | Task task = await getTaskById(id); 523 | task.copyWith( 524 | status: status, 525 | ); 526 | await updateTask(task); 527 | return task; 528 | } 529 | 530 | Future modifyTask(Task task) { 531 | return update(tasks).replace(task); 532 | } 533 | 534 | /// 表中数据改变,会发生一个流 535 | Stream> watchEntriesInCategory() { 536 | return select(tasks).watch(); 537 | } 538 | } 539 | ``` 540 | 541 | 542 | ## 总结 543 | 544 | 从路由管理到依赖注入,再到状态管理,还有 Service ,这个应用都应用到了,并轻松的实现了代码解耦。再加上骚粉的 UI ,是不错新手学习项目。 545 | 546 | 547 | todo: 548 | * 显示密码 549 | * 退出登录 550 | * 拆分网络请求和本地存储 551 | * 个人中心大图浏览 552 | * 国际化 553 | * 切换主题 554 | * 修改图标 555 | 。。。 556 | ![Simulator Screen Shot - iPhone 11 - 2020-12-12 at 15.37.53.png](https://upload-images.jianshu.io/upload_images/1454742-7a640f2b5d22556f.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 557 | 558 | ![Simulator Screen Shot - iPhone 11 - 2020-12-12 at 15.39.56.png](https://upload-images.jianshu.io/upload_images/1454742-f4f0318403197f6f.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 559 | 560 | 561 | 562 | -------------------------------------------------------------------------------- /pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | _fe_analyzer_shared: 5 | dependency: transitive 6 | description: 7 | name: _fe_analyzer_shared 8 | url: "https://pub.flutter-io.cn" 9 | source: hosted 10 | version: "12.0.0" 11 | analyzer: 12 | dependency: transitive 13 | description: 14 | name: analyzer 15 | url: "https://pub.flutter-io.cn" 16 | source: hosted 17 | version: "0.40.6" 18 | analyzer_plugin_fork: 19 | dependency: transitive 20 | description: 21 | name: analyzer_plugin_fork 22 | url: "https://pub.flutter-io.cn" 23 | source: hosted 24 | version: "0.3.0" 25 | args: 26 | dependency: transitive 27 | description: 28 | name: args 29 | url: "https://pub.flutter-io.cn" 30 | source: hosted 31 | version: "1.6.0" 32 | async: 33 | dependency: transitive 34 | description: 35 | name: async 36 | url: "https://pub.flutter-io.cn" 37 | source: hosted 38 | version: "2.5.0-nullsafety.1" 39 | boolean_selector: 40 | dependency: transitive 41 | description: 42 | name: boolean_selector 43 | url: "https://pub.flutter-io.cn" 44 | source: hosted 45 | version: "2.1.0-nullsafety.1" 46 | build: 47 | dependency: transitive 48 | description: 49 | name: build 50 | url: "https://pub.flutter-io.cn" 51 | source: hosted 52 | version: "1.6.0" 53 | build_config: 54 | dependency: transitive 55 | description: 56 | name: build_config 57 | url: "https://pub.flutter-io.cn" 58 | source: hosted 59 | version: "0.4.5" 60 | build_daemon: 61 | dependency: transitive 62 | description: 63 | name: build_daemon 64 | url: "https://pub.flutter-io.cn" 65 | source: hosted 66 | version: "2.1.4" 67 | build_resolvers: 68 | dependency: transitive 69 | description: 70 | name: build_resolvers 71 | url: "https://pub.flutter-io.cn" 72 | source: hosted 73 | version: "1.5.1" 74 | build_runner: 75 | dependency: "direct dev" 76 | description: 77 | name: build_runner 78 | url: "https://pub.flutter-io.cn" 79 | source: hosted 80 | version: "1.10.11" 81 | build_runner_core: 82 | dependency: transitive 83 | description: 84 | name: build_runner_core 85 | url: "https://pub.flutter-io.cn" 86 | source: hosted 87 | version: "6.1.5" 88 | built_collection: 89 | dependency: transitive 90 | description: 91 | name: built_collection 92 | url: "https://pub.flutter-io.cn" 93 | source: hosted 94 | version: "4.3.2" 95 | built_value: 96 | dependency: transitive 97 | description: 98 | name: built_value 99 | url: "https://pub.flutter-io.cn" 100 | source: hosted 101 | version: "7.1.0" 102 | characters: 103 | dependency: transitive 104 | description: 105 | name: characters 106 | url: "https://pub.flutter-io.cn" 107 | source: hosted 108 | version: "1.1.0-nullsafety.3" 109 | charcode: 110 | dependency: transitive 111 | description: 112 | name: charcode 113 | url: "https://pub.flutter-io.cn" 114 | source: hosted 115 | version: "1.2.0-nullsafety.1" 116 | checked_yaml: 117 | dependency: transitive 118 | description: 119 | name: checked_yaml 120 | url: "https://pub.flutter-io.cn" 121 | source: hosted 122 | version: "1.0.4" 123 | cli_util: 124 | dependency: transitive 125 | description: 126 | name: cli_util 127 | url: "https://pub.flutter-io.cn" 128 | source: hosted 129 | version: "0.1.4" 130 | clock: 131 | dependency: transitive 132 | description: 133 | name: clock 134 | url: "https://pub.flutter-io.cn" 135 | source: hosted 136 | version: "1.1.0-nullsafety.1" 137 | code_builder: 138 | dependency: transitive 139 | description: 140 | name: code_builder 141 | url: "https://pub.flutter-io.cn" 142 | source: hosted 143 | version: "3.5.0" 144 | collection: 145 | dependency: transitive 146 | description: 147 | name: collection 148 | url: "https://pub.flutter-io.cn" 149 | source: hosted 150 | version: "1.15.0-nullsafety.3" 151 | convert: 152 | dependency: transitive 153 | description: 154 | name: convert 155 | url: "https://pub.flutter-io.cn" 156 | source: hosted 157 | version: "2.1.1" 158 | cookie_jar: 159 | dependency: "direct main" 160 | description: 161 | name: cookie_jar 162 | url: "https://pub.flutter-io.cn" 163 | source: hosted 164 | version: "1.0.1" 165 | crypto: 166 | dependency: transitive 167 | description: 168 | name: crypto 169 | url: "https://pub.flutter-io.cn" 170 | source: hosted 171 | version: "2.1.5" 172 | cupertino_icons: 173 | dependency: "direct main" 174 | description: 175 | name: cupertino_icons 176 | url: "https://pub.flutter-io.cn" 177 | source: hosted 178 | version: "1.0.0" 179 | dart_style: 180 | dependency: transitive 181 | description: 182 | name: dart_style 183 | url: "https://pub.flutter-io.cn" 184 | source: hosted 185 | version: "1.3.10" 186 | date_format: 187 | dependency: "direct main" 188 | description: 189 | name: date_format 190 | url: "https://pub.flutter-io.cn" 191 | source: hosted 192 | version: "1.0.9" 193 | dio: 194 | dependency: "direct main" 195 | description: 196 | name: dio 197 | url: "https://pub.flutter-io.cn" 198 | source: hosted 199 | version: "3.0.10" 200 | dio_cookie_manager: 201 | dependency: "direct main" 202 | description: 203 | name: dio_cookie_manager 204 | url: "https://pub.flutter-io.cn" 205 | source: hosted 206 | version: "1.0.0" 207 | dio_http_cache: 208 | dependency: "direct main" 209 | description: 210 | name: dio_http_cache 211 | url: "https://pub.flutter-io.cn" 212 | source: hosted 213 | version: "0.2.11" 214 | fake_async: 215 | dependency: transitive 216 | description: 217 | name: fake_async 218 | url: "https://pub.flutter-io.cn" 219 | source: hosted 220 | version: "1.2.0-nullsafety.1" 221 | ffi: 222 | dependency: transitive 223 | description: 224 | name: ffi 225 | url: "https://pub.flutter-io.cn" 226 | source: hosted 227 | version: "0.1.3" 228 | file: 229 | dependency: transitive 230 | description: 231 | name: file 232 | url: "https://pub.flutter-io.cn" 233 | source: hosted 234 | version: "5.2.1" 235 | fixnum: 236 | dependency: transitive 237 | description: 238 | name: fixnum 239 | url: "https://pub.flutter-io.cn" 240 | source: hosted 241 | version: "0.10.11" 242 | flutter: 243 | dependency: "direct main" 244 | description: flutter 245 | source: sdk 246 | version: "0.0.0" 247 | flutter_slidable: 248 | dependency: "direct main" 249 | description: 250 | name: flutter_slidable 251 | url: "https://pub.flutter-io.cn" 252 | source: hosted 253 | version: "0.5.7" 254 | flutter_test: 255 | dependency: "direct dev" 256 | description: flutter 257 | source: sdk 258 | version: "0.0.0" 259 | flutter_web_plugins: 260 | dependency: transitive 261 | description: flutter 262 | source: sdk 263 | version: "0.0.0" 264 | get: 265 | dependency: "direct main" 266 | description: 267 | name: get 268 | url: "https://pub.flutter-io.cn" 269 | source: hosted 270 | version: "3.23.1" 271 | glob: 272 | dependency: transitive 273 | description: 274 | name: glob 275 | url: "https://pub.flutter-io.cn" 276 | source: hosted 277 | version: "1.2.0" 278 | google_fonts: 279 | dependency: "direct main" 280 | description: 281 | name: google_fonts 282 | url: "https://pub.flutter-io.cn" 283 | source: hosted 284 | version: "1.1.1" 285 | graphs: 286 | dependency: transitive 287 | description: 288 | name: graphs 289 | url: "https://pub.flutter-io.cn" 290 | source: hosted 291 | version: "0.2.0" 292 | http: 293 | dependency: transitive 294 | description: 295 | name: http 296 | url: "https://pub.flutter-io.cn" 297 | source: hosted 298 | version: "0.12.2" 299 | http_multi_server: 300 | dependency: transitive 301 | description: 302 | name: http_multi_server 303 | url: "https://pub.flutter-io.cn" 304 | source: hosted 305 | version: "2.2.0" 306 | http_parser: 307 | dependency: transitive 308 | description: 309 | name: http_parser 310 | url: "https://pub.flutter-io.cn" 311 | source: hosted 312 | version: "3.1.4" 313 | intl: 314 | dependency: transitive 315 | description: 316 | name: intl 317 | url: "https://pub.flutter-io.cn" 318 | source: hosted 319 | version: "0.16.1" 320 | io: 321 | dependency: transitive 322 | description: 323 | name: io 324 | url: "https://pub.flutter-io.cn" 325 | source: hosted 326 | version: "0.3.4" 327 | js: 328 | dependency: transitive 329 | description: 330 | name: js 331 | url: "https://pub.flutter-io.cn" 332 | source: hosted 333 | version: "0.6.2" 334 | json_annotation: 335 | dependency: transitive 336 | description: 337 | name: json_annotation 338 | url: "https://pub.flutter-io.cn" 339 | source: hosted 340 | version: "3.1.1" 341 | json_serializable: 342 | dependency: transitive 343 | description: 344 | name: json_serializable 345 | url: "https://pub.flutter-io.cn" 346 | source: hosted 347 | version: "3.5.1" 348 | logging: 349 | dependency: transitive 350 | description: 351 | name: logging 352 | url: "https://pub.flutter-io.cn" 353 | source: hosted 354 | version: "0.11.4" 355 | matcher: 356 | dependency: transitive 357 | description: 358 | name: matcher 359 | url: "https://pub.flutter-io.cn" 360 | source: hosted 361 | version: "0.12.10-nullsafety.1" 362 | meta: 363 | dependency: transitive 364 | description: 365 | name: meta 366 | url: "https://pub.flutter-io.cn" 367 | source: hosted 368 | version: "1.3.0-nullsafety.3" 369 | mime: 370 | dependency: transitive 371 | description: 372 | name: mime 373 | url: "https://pub.flutter-io.cn" 374 | source: hosted 375 | version: "0.9.7" 376 | moor: 377 | dependency: "direct main" 378 | description: 379 | name: moor 380 | url: "https://pub.flutter-io.cn" 381 | source: hosted 382 | version: "3.4.0" 383 | moor_generator: 384 | dependency: "direct dev" 385 | description: 386 | name: moor_generator 387 | url: "https://pub.flutter-io.cn" 388 | source: hosted 389 | version: "3.4.0" 390 | node_interop: 391 | dependency: transitive 392 | description: 393 | name: node_interop 394 | url: "https://pub.flutter-io.cn" 395 | source: hosted 396 | version: "1.2.1" 397 | node_io: 398 | dependency: transitive 399 | description: 400 | name: node_io 401 | url: "https://pub.flutter-io.cn" 402 | source: hosted 403 | version: "1.2.0" 404 | package_config: 405 | dependency: transitive 406 | description: 407 | name: package_config 408 | url: "https://pub.flutter-io.cn" 409 | source: hosted 410 | version: "1.9.3" 411 | path: 412 | dependency: "direct main" 413 | description: 414 | name: path 415 | url: "https://pub.flutter-io.cn" 416 | source: hosted 417 | version: "1.8.0-nullsafety.1" 418 | path_provider: 419 | dependency: "direct main" 420 | description: 421 | name: path_provider 422 | url: "https://pub.flutter-io.cn" 423 | source: hosted 424 | version: "1.6.24" 425 | path_provider_linux: 426 | dependency: transitive 427 | description: 428 | name: path_provider_linux 429 | url: "https://pub.flutter-io.cn" 430 | source: hosted 431 | version: "0.0.1+2" 432 | path_provider_macos: 433 | dependency: transitive 434 | description: 435 | name: path_provider_macos 436 | url: "https://pub.flutter-io.cn" 437 | source: hosted 438 | version: "0.0.4+6" 439 | path_provider_platform_interface: 440 | dependency: transitive 441 | description: 442 | name: path_provider_platform_interface 443 | url: "https://pub.flutter-io.cn" 444 | source: hosted 445 | version: "1.0.4" 446 | path_provider_windows: 447 | dependency: transitive 448 | description: 449 | name: path_provider_windows 450 | url: "https://pub.flutter-io.cn" 451 | source: hosted 452 | version: "0.0.4+3" 453 | pedantic: 454 | dependency: transitive 455 | description: 456 | name: pedantic 457 | url: "https://pub.flutter-io.cn" 458 | source: hosted 459 | version: "1.9.2" 460 | platform: 461 | dependency: transitive 462 | description: 463 | name: platform 464 | url: "https://pub.flutter-io.cn" 465 | source: hosted 466 | version: "2.2.1" 467 | plugin_platform_interface: 468 | dependency: transitive 469 | description: 470 | name: plugin_platform_interface 471 | url: "https://pub.flutter-io.cn" 472 | source: hosted 473 | version: "1.0.3" 474 | pool: 475 | dependency: transitive 476 | description: 477 | name: pool 478 | url: "https://pub.flutter-io.cn" 479 | source: hosted 480 | version: "1.4.0" 481 | process: 482 | dependency: transitive 483 | description: 484 | name: process 485 | url: "https://pub.flutter-io.cn" 486 | source: hosted 487 | version: "3.0.13" 488 | pub_semver: 489 | dependency: transitive 490 | description: 491 | name: pub_semver 492 | url: "https://pub.flutter-io.cn" 493 | source: hosted 494 | version: "1.4.4" 495 | pubspec_parse: 496 | dependency: transitive 497 | description: 498 | name: pubspec_parse 499 | url: "https://pub.flutter-io.cn" 500 | source: hosted 501 | version: "0.1.7" 502 | pull_to_refresh: 503 | dependency: "direct main" 504 | description: 505 | name: pull_to_refresh 506 | url: "https://pub.flutter-io.cn" 507 | source: hosted 508 | version: "1.6.3" 509 | quiver: 510 | dependency: transitive 511 | description: 512 | name: quiver 513 | url: "https://pub.flutter-io.cn" 514 | source: hosted 515 | version: "2.1.5" 516 | recase: 517 | dependency: transitive 518 | description: 519 | name: recase 520 | url: "https://pub.flutter-io.cn" 521 | source: hosted 522 | version: "3.0.1" 523 | shared_preferences: 524 | dependency: "direct main" 525 | description: 526 | name: shared_preferences 527 | url: "https://pub.flutter-io.cn" 528 | source: hosted 529 | version: "0.5.12+4" 530 | shared_preferences_linux: 531 | dependency: transitive 532 | description: 533 | name: shared_preferences_linux 534 | url: "https://pub.flutter-io.cn" 535 | source: hosted 536 | version: "0.0.2+4" 537 | shared_preferences_macos: 538 | dependency: transitive 539 | description: 540 | name: shared_preferences_macos 541 | url: "https://pub.flutter-io.cn" 542 | source: hosted 543 | version: "0.0.1+11" 544 | shared_preferences_platform_interface: 545 | dependency: transitive 546 | description: 547 | name: shared_preferences_platform_interface 548 | url: "https://pub.flutter-io.cn" 549 | source: hosted 550 | version: "1.0.4" 551 | shared_preferences_web: 552 | dependency: transitive 553 | description: 554 | name: shared_preferences_web 555 | url: "https://pub.flutter-io.cn" 556 | source: hosted 557 | version: "0.1.2+7" 558 | shared_preferences_windows: 559 | dependency: transitive 560 | description: 561 | name: shared_preferences_windows 562 | url: "https://pub.flutter-io.cn" 563 | source: hosted 564 | version: "0.0.1+3" 565 | shelf: 566 | dependency: transitive 567 | description: 568 | name: shelf 569 | url: "https://pub.flutter-io.cn" 570 | source: hosted 571 | version: "0.7.9" 572 | shelf_web_socket: 573 | dependency: transitive 574 | description: 575 | name: shelf_web_socket 576 | url: "https://pub.flutter-io.cn" 577 | source: hosted 578 | version: "0.2.3" 579 | simple_gesture_detector: 580 | dependency: transitive 581 | description: 582 | name: simple_gesture_detector 583 | url: "https://pub.flutter-io.cn" 584 | source: hosted 585 | version: "0.1.5" 586 | sky_engine: 587 | dependency: transitive 588 | description: flutter 589 | source: sdk 590 | version: "0.0.99" 591 | source_gen: 592 | dependency: transitive 593 | description: 594 | name: source_gen 595 | url: "https://pub.flutter-io.cn" 596 | source: hosted 597 | version: "0.9.10+1" 598 | source_span: 599 | dependency: transitive 600 | description: 601 | name: source_span 602 | url: "https://pub.flutter-io.cn" 603 | source: hosted 604 | version: "1.8.0-nullsafety.2" 605 | sqflite: 606 | dependency: transitive 607 | description: 608 | name: sqflite 609 | url: "https://pub.flutter-io.cn" 610 | source: hosted 611 | version: "1.3.2+1" 612 | sqflite_common: 613 | dependency: transitive 614 | description: 615 | name: sqflite_common 616 | url: "https://pub.flutter-io.cn" 617 | source: hosted 618 | version: "1.0.2+1" 619 | sqlite3: 620 | dependency: transitive 621 | description: 622 | name: sqlite3 623 | url: "https://pub.flutter-io.cn" 624 | source: hosted 625 | version: "0.1.8" 626 | sqlite3_flutter_libs: 627 | dependency: "direct main" 628 | description: 629 | name: sqlite3_flutter_libs 630 | url: "https://pub.flutter-io.cn" 631 | source: hosted 632 | version: "0.3.0" 633 | sqlparser: 634 | dependency: transitive 635 | description: 636 | name: sqlparser 637 | url: "https://pub.flutter-io.cn" 638 | source: hosted 639 | version: "0.11.0" 640 | stack_trace: 641 | dependency: transitive 642 | description: 643 | name: stack_trace 644 | url: "https://pub.flutter-io.cn" 645 | source: hosted 646 | version: "1.10.0-nullsafety.1" 647 | stream_channel: 648 | dependency: transitive 649 | description: 650 | name: stream_channel 651 | url: "https://pub.flutter-io.cn" 652 | source: hosted 653 | version: "2.1.0-nullsafety.1" 654 | stream_transform: 655 | dependency: transitive 656 | description: 657 | name: stream_transform 658 | url: "https://pub.flutter-io.cn" 659 | source: hosted 660 | version: "1.2.0" 661 | string_scanner: 662 | dependency: transitive 663 | description: 664 | name: string_scanner 665 | url: "https://pub.flutter-io.cn" 666 | source: hosted 667 | version: "1.1.0-nullsafety.1" 668 | synchronized: 669 | dependency: transitive 670 | description: 671 | name: synchronized 672 | url: "https://pub.flutter-io.cn" 673 | source: hosted 674 | version: "2.2.0+2" 675 | table_calendar: 676 | dependency: "direct main" 677 | description: 678 | name: table_calendar 679 | url: "https://pub.flutter-io.cn" 680 | source: hosted 681 | version: "2.3.3" 682 | term_glyph: 683 | dependency: transitive 684 | description: 685 | name: term_glyph 686 | url: "https://pub.flutter-io.cn" 687 | source: hosted 688 | version: "1.2.0-nullsafety.1" 689 | test_api: 690 | dependency: transitive 691 | description: 692 | name: test_api 693 | url: "https://pub.flutter-io.cn" 694 | source: hosted 695 | version: "0.2.19-nullsafety.2" 696 | timing: 697 | dependency: transitive 698 | description: 699 | name: timing 700 | url: "https://pub.flutter-io.cn" 701 | source: hosted 702 | version: "0.1.1+3" 703 | typed_data: 704 | dependency: transitive 705 | description: 706 | name: typed_data 707 | url: "https://pub.flutter-io.cn" 708 | source: hosted 709 | version: "1.3.0-nullsafety.3" 710 | vector_math: 711 | dependency: transitive 712 | description: 713 | name: vector_math 714 | url: "https://pub.flutter-io.cn" 715 | source: hosted 716 | version: "2.1.0-nullsafety.3" 717 | watcher: 718 | dependency: transitive 719 | description: 720 | name: watcher 721 | url: "https://pub.flutter-io.cn" 722 | source: hosted 723 | version: "0.9.7+15" 724 | web_socket_channel: 725 | dependency: transitive 726 | description: 727 | name: web_socket_channel 728 | url: "https://pub.flutter-io.cn" 729 | source: hosted 730 | version: "1.1.0" 731 | win32: 732 | dependency: transitive 733 | description: 734 | name: win32 735 | url: "https://pub.flutter-io.cn" 736 | source: hosted 737 | version: "1.7.4" 738 | xdg_directories: 739 | dependency: transitive 740 | description: 741 | name: xdg_directories 742 | url: "https://pub.flutter-io.cn" 743 | source: hosted 744 | version: "0.1.2" 745 | yaml: 746 | dependency: transitive 747 | description: 748 | name: yaml 749 | url: "https://pub.flutter-io.cn" 750 | source: hosted 751 | version: "2.2.1" 752 | sdks: 753 | dart: ">=2.10.2 <2.11.0" 754 | flutter: ">=1.22.2 <2.0.0" 755 | --------------------------------------------------------------------------------