diff --git a/.gitignore b/.gitignore
index d386c3123..efda2ecd4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -44,3 +44,4 @@ buildNumber.properties
 *.iml
 out
 gen
+*DS_Store
diff --git a/README.md b/README.md
index 8b4f079eb..60b61dd9b 100644
--- a/README.md
+++ b/README.md
@@ -4,6 +4,10 @@
 
 本项目为全栈工程师学习笔记,不求最深最好,开源技术搬运工
 
+https://github.com/GrowingGit/GitHub-Chinese-Top-Charts 开源API 
+
+
+
 其他项目:
 Spring Cloud 微服务学习笔记 [https://github.com/bage2014/study-micro-services](https://github.com/bage2014/study-micro-services)
 
@@ -25,7 +29,7 @@ AOP实用技巧;比如打日志等
 m3u parser https://github.com/BjoernPetersen/m3u-parser
 
 
-知识汇总::::
+知识汇总:
 
 https://github.com/CyC2018/CS-Notes
 
@@ -49,9 +53,21 @@ https://github.com/jobbole/awesome-java-cn
 
 https://mp.weixin.qq.com/s/kro_gyUQrdKSy1x3ETwb5A
 
+限流组件
+
+重试组件
+
 
 
 ## Others ##
+小工具??
+
+https://github.com/jgraph/drawio-desktop/releases/tag/v20.3.0
+
+
+
+
+
 GIT 地址
 https://git-scm.com/book/zh/v2/%E8%B5%B7%E6%AD%A5-%E5%AE%89%E8%A3%85-Git
 
diff --git a/README_Template.md b/README_Template.md
new file mode 100644
index 000000000..58ca749f5
--- /dev/null
+++ b/README_Template.md
@@ -0,0 +1,35 @@
+# study- XXX
+
+文档编写目的
+
+当前进度
+
+后期规划
+
+项目代办
+
+## 参考链接
+
+官网文档 XXX
+
+## 快速开始
+
+下载运行
+
+```
+Publish Remote
+
+```
+
+## 应用实践
+
+下载运行
+
+```
+Publish Remote
+
+```
+
+## 原理解析
+
+基本原理
\ No newline at end of file
diff --git a/bage-spring-boot-starter-ping-test/pom.xml b/bage-spring-boot-starter-ping-test/pom.xml
index 3304759e8..cbcef443e 100644
--- a/bage-spring-boot-starter-ping-test/pom.xml
+++ b/bage-spring-boot-starter-ping-test/pom.xml
@@ -15,7 +15,9 @@
     http://www.example.com
 
     
-        UTF-8
+        1.8
+        1.8
+
         2.0.1.RELEASE
     
     
diff --git a/bage-spring-boot-starter-ping/README.md b/bage-spring-boot-starter-ping/README.md
index 0f06dc689..e92d4f7ac 100644
--- a/bage-spring-boot-starter-ping/README.md
+++ b/bage-spring-boot-starter-ping/README.md
@@ -7,6 +7,8 @@
 ```
 
     UTF-8
+        1.8
+        1.8
     2.0.1.RELEASE
 
 
diff --git a/bage-spring-boot-starter-ping/pom.xml b/bage-spring-boot-starter-ping/pom.xml
index 231aa7e42..45e6d816e 100644
--- a/bage-spring-boot-starter-ping/pom.xml
+++ b/bage-spring-boot-starter-ping/pom.xml
@@ -15,7 +15,9 @@
     http://www.example.com
 
     
-        UTF-8
+        1.8
+        1.8
+
         2.0.1.RELEASE
     
     
diff --git a/pom.xml b/pom.xml
index f4978018f..f38051e52 100644
--- a/pom.xml
+++ b/pom.xml
@@ -16,6 +16,8 @@
 
   
     UTF-8
+        1.8
+        1.8
   
   
   	study-spring-boot
@@ -51,6 +53,7 @@
     study-gateway
     study-sql
     study-quartz
+    study-guava
     study-shiro-web-tutorial
     study-rabbitmq
     study-tomcat
@@ -135,7 +138,9 @@
       study-summary
       study-id-generator
       study-stream-m3u8
+
       study-hystrix
       study-resilience4j
     study-ews-java-api
@@ -144,7 +149,27 @@
       study-sftp
       study-jmockit
       study-mockito
+
+    study-file-online-preview
+    study-jol
+      study-servlet
+      study-lombok
+      study-spring-boot-elk
+      study-mapstruct
+      study-sentinel
+      study-mybatis-plus
+      study-oceanbase
+      study-spring-boot-trace
+      study-spring-boot-trace-server
+      study-spring-boot-wavefront
+      study-spring-boot-valid
+      study-spring-boot-web-socket
+    study-spring-cloud-circuit-breaker
+    study-crawler
+      study-jenkins
+      study-nginx
 
   
 
\ No newline at end of file
diff --git a/study-activemq/pom.xml b/study-activemq/pom.xml
index b9c5f4a01..56365c9bc 100644
--- a/study-activemq/pom.xml
+++ b/study-activemq/pom.xml
@@ -11,7 +11,9 @@
   study-activemq
   http://maven.apache.org
   
-    UTF-8
+
+    1.8
+    1.8
   
   
     
diff --git a/study-algorithm/pom.xml b/study-algorithm/pom.xml
index 727d0b784..5e77e61e9 100644
--- a/study-algorithm/pom.xml
+++ b/study-algorithm/pom.xml
@@ -11,7 +11,9 @@
   study-algorithm
   http://maven.apache.org
   
-    UTF-8
+
+    1.8
+    1.8
   
   
     
diff --git a/study-algorithm/src/main/java/com/bage/study/algorithm/leetcode/MinDeepTree.java b/study-algorithm/src/main/java/com/bage/study/algorithm/leetcode/MinDeepTree.java
new file mode 100644
index 000000000..040f92cb5
--- /dev/null
+++ b/study-algorithm/src/main/java/com/bage/study/algorithm/leetcode/MinDeepTree.java
@@ -0,0 +1,24 @@
+package com.bage.study.algorithm.leetcode;
+
+import java.util.Objects;
+
+
+/**
+ * https://leetcode-cn.com/problems/minimum-depth-of-binary-tree/
+ * // todo ddd
+ */
+public class MinDeepTree {
+    public ListNode reverseList(ListNode head) {
+        if(Objects.isNull(head) || Objects.isNull(head.next)){
+            return head;
+        }
+        ListNode listNode = reverseList(head.next);
+        head.next.next = head;
+        head.next = null;
+        return listNode;
+    }
+}
+
+class TreeNode {
+    TreeNode next;
+ }
diff --git a/study-algorithm/src/main/java/com/bage/study/algorithm/leetcode/RevertNodes.java b/study-algorithm/src/main/java/com/bage/study/algorithm/leetcode/RevertNodes.java
new file mode 100644
index 000000000..8b11f3a5e
--- /dev/null
+++ b/study-algorithm/src/main/java/com/bage/study/algorithm/leetcode/RevertNodes.java
@@ -0,0 +1,39 @@
+package com.bage.study.algorithm.leetcode;
+
+import java.util.Objects;
+
+/**
+ * https://leetcode-cn.com/problems/UHnkqh/
+ */
+public class RevertNodes {
+}
+
+/**
+ * Definition for singly-linked list.
+ * public class ListNode {
+ *     int val;
+ *     ListNode next;
+ *     ListNode() {}
+ *     ListNode(int val) { this.val = val; }
+ *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
+ * }
+ */
+class Solution {
+    public ListNode reverseList(ListNode head) {
+        if(Objects.isNull(head) || Objects.isNull(head.next)){
+            return head;
+        }
+        ListNode listNode = reverseList(head.next);
+        head.next.next = head;
+        head.next = null;
+        return listNode;
+    }
+}
+
+ class ListNode {
+      int val;
+      ListNode next;
+      ListNode() {}
+      ListNode(int val) { this.val = val; }
+      ListNode(int val, ListNode next) { this.val = val; this.next = next; }
+  }
diff --git a/study-algorithm/src/main/java/com/bage/study/algorithm/leetcode/SortedArraySum.java b/study-algorithm/src/main/java/com/bage/study/algorithm/leetcode/SortedArraySum.java
new file mode 100644
index 000000000..7ea6c3c56
--- /dev/null
+++ b/study-algorithm/src/main/java/com/bage/study/algorithm/leetcode/SortedArraySum.java
@@ -0,0 +1,19 @@
+package com.bage.study.algorithm.leetcode;
+
+import java.util.Objects;
+
+/**
+ * https://leetcode-cn.com/problems/kLl5u1/
+ * SortedArraySum
+ */
+public class SortedArraySum {
+    public ListNode reverseList(ListNode head) {
+        if(Objects.isNull(head) || Objects.isNull(head.next)){
+            return head;
+        }
+        ListNode listNode = reverseList(head.next);
+        head.next.next = head;
+        head.next = null;
+        return listNode;
+    }
+}
diff --git a/study-app-tutorials/.gitignore b/study-app-tutorials/.gitignore
new file mode 100644
index 000000000..0fa6b675c
--- /dev/null
+++ b/study-app-tutorials/.gitignore
@@ -0,0 +1,46 @@
+# Miscellaneous
+*.class
+*.log
+*.pyc
+*.swp
+.DS_Store
+.atom/
+.buildlog/
+.history
+.svn/
+
+# IntelliJ related
+*.iml
+*.ipr
+*.iws
+.idea/
+
+# The .vscode folder contains launch configuration and tasks you configure in
+# VS Code which you may wish to be included in version control, so this line
+# is commented out by default.
+#.vscode/
+
+# Flutter/Dart/Pub related
+**/doc/api/
+**/ios/Flutter/.last_build_id
+.dart_tool/
+.flutter-plugins
+.flutter-plugins-dependencies
+.packages
+.pub-cache/
+.pub/
+/build/
+
+# Web related
+lib/generated_plugin_registrant.dart
+
+# Symbolication related
+app.*.symbols
+
+# Obfuscation related
+app.*.map.json
+
+# Android Studio will place build artifacts here
+/android/app/debug
+/android/app/profile
+/android/app/release
diff --git a/study-app-tutorials/.metadata b/study-app-tutorials/.metadata
new file mode 100644
index 000000000..a5584fc37
--- /dev/null
+++ b/study-app-tutorials/.metadata
@@ -0,0 +1,10 @@
+# This file tracks properties of this Flutter project.
+# Used by Flutter tool to assess capabilities and perform upgrades etc.
+#
+# This file should be version controlled and should not be manually edited.
+
+version:
+  revision: 18116933e77adc82f80866c928266a5b4f1ed645
+  channel: stable
+
+project_type: app
diff --git a/study-app-tutorials/README.md b/study-app-tutorials/README.md
new file mode 100644
index 000000000..200cf352e
--- /dev/null
+++ b/study-app-tutorials/README.md
@@ -0,0 +1,169 @@
+# tutorials
+
+A tutorials project.
+
+## todo
+登陆实效、1个月
+
+个人名片
+商用名片
+家用名片
+
+标签管理
+名片管理
+
+
+个人经典事迹?? 别人点赞,验证真伪!!
+
+我的音乐?视频??啥的??
+
+自定义名片
+
+文件上传
+
+Mock 服务,,比如 10% 出现404
+
+名片页面设计
+
+
+
+2022年结束---上线APP
+
+
+
+## 参考资料
+
+https://javiercbk.github.io/json_to_dart/
+https://jsontodart.com/
+
+Flutter实战 https://book.flutterchina.club/
+
+https://flutter.cn/
+
+https://docs.flutter.dev/development/ui/widgets/material
+
+带源码【组件 + 源码】
+很多很赞
+https://github.com/bukunmialuko/flutter_ui_kit_obkm
+
+很多很赞
+https://github.com/anoochit/uikits
+
+很多很赞
+https://github.com/lohanidamodar/flutter_ui_challenges
+
+常用页面
+https://github.com/simplezhli/flutter_deer
+
+应用集合
+https://github.com/Vignesh0404/Flutter-UI-Kit
+
+https://github.com/vinothvino42/SwiggyUI
+
+https://github.com/usman18/Flutter-UI-Kit
+
+
+
+官网常用
+https://docs.flutter.dev/development/ui/widgets/material
+
+https://doc.flutterchina.club/widgets/
+
+
+
+页面合集
+
+
+https://fluttertemplates.dev/
+
+https://github.com/iampawan/Flutter-UI-Kit
+
+https://github.com/olayemii/flutter-ui-kits
+
+https://www.behance.net/gallery/69411833/Backpack-UI-Kit-Free-for-Adobe-XD UI资源 很多
+
+
+
+闹钟、通知类
+
+https://pub.flutter-io.cn/packages/flutter_local_notifications
+
+https://pub.flutter-io.cn/packages/awesome_notifications
+https://pub.dev/packages/android_alarm_manager_plus
+
+
+Flutter UI 
+
+https://pub.flutter-io.cn/packages/fluent_ui/example
+https://bdlukaa.github.io/fluent_ui/
+
+
+
+
+FLutter Icon
+
+https://www.fluttericon.cn/
+
+
+生成签名
+```csharp
+keytool -genkey -v -keystore ./sign.jks -keyalg RSA -keysize 2048 -validity 10000 -alias sign
+```
+
+输入密码
+bage.app
+
+创建文件
+key.properties
+```csharp
+storePassword=bage.app
+keyPassword=bage.app
+keyAlias=sign
+storeFile=sign.jks
+```
+
+build.gradle
+```csharp
+
+def keystorePropertiesFile = rootProject.file("app/key/key.properties")
+def keystoreProperties = new Properties()
+keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
+
+
+android 节点下:
+
+    signingConfigs {
+            release {
+                keyAlias 'sign'
+                keyPassword 'bage.app'
+                storeFile file('key/sign.jks')
+                storePassword 'android'
+            }
+        }
+
+        buildTypes {
+            release {
+                // TODO: Add your own signing config for the release build.
+                // Signing with the debug keys for now, so `flutter run --release` works.
+                signingConfig signingConfigs.release
+                // signingConfig signingConfigs.debug
+
+            }
+        }
+
+```
+
+
+
+后端地址
+
+10.0.2.2
+
+
+
+账号:
+
+bage2014@qq.com
+
+123456
+
diff --git a/study-app-tutorials/analysis_options.yaml b/study-app-tutorials/analysis_options.yaml
new file mode 100644
index 000000000..61b6c4de1
--- /dev/null
+++ b/study-app-tutorials/analysis_options.yaml
@@ -0,0 +1,29 @@
+# This file configures the analyzer, which statically analyzes Dart code to
+# check for errors, warnings, and lints.
+#
+# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
+# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
+# invoked from the command line by running `flutter analyze`.
+
+# The following line activates a set of recommended lints for Flutter apps,
+# packages, and plugins designed to encourage good coding practices.
+include: package:flutter_lints/flutter.yaml
+
+linter:
+  # The lint rules applied to this project can be customized in the
+  # section below to disable rules from the `package:flutter_lints/flutter.yaml`
+  # included above or to enable additional rules. A list of all available lints
+  # and their documentation is published at
+  # https://dart-lang.github.io/linter/lints/index.html.
+  #
+  # Instead of disabling a lint rule for the entire project in the
+  # section below, it can also be suppressed for a single line of code
+  # or a specific dart file by using the `// ignore: name_of_lint` and
+  # `// ignore_for_file: name_of_lint` syntax on the line or in the file
+  # producing the lint.
+  rules:
+    # avoid_print: false  # Uncomment to disable the `avoid_print` rule
+    # prefer_single_quotes: true  # Uncomment to enable the `prefer_single_quotes` rule
+
+# Additional information about this file can be found at
+# https://dart.dev/guides/language/analysis-options
diff --git a/study-app-tutorials/android/.gitignore b/study-app-tutorials/android/.gitignore
new file mode 100644
index 000000000..2d60e8488
--- /dev/null
+++ b/study-app-tutorials/android/.gitignore
@@ -0,0 +1,13 @@
+gradle-wrapper.jar
+/.gradle
+/captures/
+/gradlew
+/gradlew.bat
+/local.properties
+GeneratedPluginRegistrant.java
+
+# Remember to never publicly share your keystore.
+# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
+app/key/key.properties
+**/*.keystore
+**/*.jks
diff --git a/study-app-tutorials/android/app/build.gradle b/study-app-tutorials/android/app/build.gradle
new file mode 100644
index 000000000..4a26158c4
--- /dev/null
+++ b/study-app-tutorials/android/app/build.gradle
@@ -0,0 +1,72 @@
+
+def keystorePropertiesFile = rootProject.file("app/key/key.properties")
+def keystoreProperties = new Properties()
+keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
+
+
+def localProperties = new Properties()
+def localPropertiesFile = rootProject.file('local.properties')
+if (localPropertiesFile.exists()) {
+    localPropertiesFile.withReader('UTF-8') { reader ->
+        localProperties.load(reader)
+    }
+}
+
+def flutterRoot = localProperties.getProperty('flutter.sdk')
+if (flutterRoot == null) {
+    throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
+}
+
+def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
+if (flutterVersionCode == null) {
+    flutterVersionCode = '1'
+}
+
+def flutterVersionName = localProperties.getProperty('flutter.versionName')
+if (flutterVersionName == null) {
+    flutterVersionName = '1.0'
+}
+
+apply plugin: 'com.android.application'
+apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
+
+android {
+    compileSdkVersion 31
+
+    compileOptions {
+        sourceCompatibility JavaVersion.VERSION_1_8
+        targetCompatibility JavaVersion.VERSION_1_8
+    }
+
+    defaultConfig {
+        // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
+        applicationId "com.bage.tutorials"
+        minSdkVersion 16
+        targetSdkVersion 31
+        versionCode flutterVersionCode.toInteger()
+        versionName flutterVersionName
+    }
+
+    signingConfigs {
+        release {
+            keyAlias 'sign'
+            keyPassword 'bage.app'
+            storeFile file('key/sign.jks')
+            storePassword 'bage.app'
+        }
+    }
+
+    buildTypes {
+        release {
+            // TODO: Add your own signing config for the release build.
+            // Signing with the debug keys for now, so `flutter run --release` works.
+            signingConfig signingConfigs.release
+            // signingConfig signingConfigs.debug
+
+        }
+    }
+}
+
+flutter {
+    source '../..'
+}
diff --git a/study-app-tutorials/android/app/src/debug/AndroidManifest.xml b/study-app-tutorials/android/app/src/debug/AndroidManifest.xml
new file mode 100644
index 000000000..31b30612d
--- /dev/null
+++ b/study-app-tutorials/android/app/src/debug/AndroidManifest.xml
@@ -0,0 +1,7 @@
+
+    
+    
+
diff --git a/study-app-tutorials/android/app/src/main/AndroidManifest.xml b/study-app-tutorials/android/app/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..f21ed8597
--- /dev/null
+++ b/study-app-tutorials/android/app/src/main/AndroidManifest.xml
@@ -0,0 +1,64 @@
+
+
+    
+    
+    
+    
+
+    
+    
+
+    
+    
+
+
+
+    
+        
+            
+            
+            
+            
+            
+                
+                
+            
+        
+        
+        
+
+
+        
+        
+
+    
+
diff --git a/study-app-tutorials/android/app/src/main/java/com/bage/tutorials/MainActivity.java b/study-app-tutorials/android/app/src/main/java/com/bage/tutorials/MainActivity.java
new file mode 100644
index 000000000..5fb42eb3d
--- /dev/null
+++ b/study-app-tutorials/android/app/src/main/java/com/bage/tutorials/MainActivity.java
@@ -0,0 +1,6 @@
+package com.bage.tutorials;
+
+import io.flutter.embedding.android.FlutterActivity;
+
+public class MainActivity extends FlutterActivity {
+}
diff --git a/study-app-tutorials/android/app/src/main/res/drawable-v21/launch_background.xml b/study-app-tutorials/android/app/src/main/res/drawable-v21/launch_background.xml
new file mode 100644
index 000000000..f74085f3f
--- /dev/null
+++ b/study-app-tutorials/android/app/src/main/res/drawable-v21/launch_background.xml
@@ -0,0 +1,12 @@
+
+
+
+    +
+    
+    
+
diff --git a/study-app-tutorials/android/app/src/main/res/drawable/launch_background.xml b/study-app-tutorials/android/app/src/main/res/drawable/launch_background.xml
new file mode 100644
index 000000000..304732f88
--- /dev/null
+++ b/study-app-tutorials/android/app/src/main/res/drawable/launch_background.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+    
+    
+
diff --git a/study-app-tutorials/android/app/src/main/res/drawable/logo128.png b/study-app-tutorials/android/app/src/main/res/drawable/logo128.png
new file mode 100644
index 000000000..bfb3553d2
Binary files /dev/null and b/study-app-tutorials/android/app/src/main/res/drawable/logo128.png differ
diff --git a/study-app-tutorials/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/study-app-tutorials/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 000000000..db77bb4b7
Binary files /dev/null and b/study-app-tutorials/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/study-app-tutorials/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/study-app-tutorials/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 000000000..17987b79b
Binary files /dev/null and b/study-app-tutorials/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/study-app-tutorials/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/study-app-tutorials/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 000000000..09d439148
Binary files /dev/null and b/study-app-tutorials/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/study-app-tutorials/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/study-app-tutorials/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 000000000..d5f1c8d34
Binary files /dev/null and b/study-app-tutorials/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/study-app-tutorials/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/study-app-tutorials/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 000000000..4d6372eeb
Binary files /dev/null and b/study-app-tutorials/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/study-app-tutorials/android/app/src/main/res/values-night/styles.xml b/study-app-tutorials/android/app/src/main/res/values-night/styles.xml
new file mode 100644
index 000000000..449a9f930
--- /dev/null
+++ b/study-app-tutorials/android/app/src/main/res/values-night/styles.xml
@@ -0,0 +1,18 @@
+
+
+    
+    
+    
+    
+
diff --git a/study-app-tutorials/android/app/src/main/res/values/styles.xml b/study-app-tutorials/android/app/src/main/res/values/styles.xml
new file mode 100644
index 000000000..d74aa35c2
--- /dev/null
+++ b/study-app-tutorials/android/app/src/main/res/values/styles.xml
@@ -0,0 +1,18 @@
+
+
+    
+    
+    
+    
+
diff --git a/study-app-tutorials/android/app/src/profile/AndroidManifest.xml b/study-app-tutorials/android/app/src/profile/AndroidManifest.xml
new file mode 100644
index 000000000..31b30612d
--- /dev/null
+++ b/study-app-tutorials/android/app/src/profile/AndroidManifest.xml
@@ -0,0 +1,7 @@
+
+    
+    
+
diff --git a/study-app-tutorials/android/build.gradle b/study-app-tutorials/android/build.gradle
new file mode 100644
index 000000000..d063075b6
--- /dev/null
+++ b/study-app-tutorials/android/build.gradle
@@ -0,0 +1,35 @@
+buildscript {
+    repositories {
+        maven { url 'https://maven.aliyun.com/repository/google' }
+        maven { url 'https://maven.aliyun.com/repository/jcenter' }
+        maven { url 'http://maven.aliyun.com/nexus/content/groups/public' }
+        
+//        google()
+//        mavenCentral()
+    }
+
+    dependencies {
+        classpath 'com.android.tools.build:gradle:4.1.0'
+    }
+}
+
+allprojects {
+    repositories {
+        maven { url 'https://maven.aliyun.com/repository/google' }
+        maven { url 'https://maven.aliyun.com/repository/jcenter' }
+        maven { url 'http://maven.aliyun.com/nexus/content/groups/public' }
+
+//        google()
+//        mavenCentral()
+    }
+}
+
+rootProject.buildDir = '../build'
+subprojects {
+    project.buildDir = "${rootProject.buildDir}/${project.name}"
+    project.evaluationDependsOn(':app')
+}
+
+task clean(type: Delete) {
+    delete rootProject.buildDir
+}
diff --git a/study-app-tutorials/android/gradle.properties b/study-app-tutorials/android/gradle.properties
new file mode 100644
index 000000000..94adc3a3f
--- /dev/null
+++ b/study-app-tutorials/android/gradle.properties
@@ -0,0 +1,3 @@
+org.gradle.jvmargs=-Xmx1536M
+android.useAndroidX=true
+android.enableJetifier=true
diff --git a/study-app-tutorials/android/gradle/wrapper/gradle-wrapper.properties b/study-app-tutorials/android/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 000000000..bc6a58afd
--- /dev/null
+++ b/study-app-tutorials/android/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Fri Jun 23 08:50:38 CEST 2017
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip
diff --git a/study-app-tutorials/android/settings.gradle b/study-app-tutorials/android/settings.gradle
new file mode 100644
index 000000000..44e62bcf0
--- /dev/null
+++ b/study-app-tutorials/android/settings.gradle
@@ -0,0 +1,11 @@
+include ':app'
+
+def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
+def properties = new Properties()
+
+assert localPropertiesFile.exists()
+localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
+
+def flutterSdkPath = properties.getProperty("flutter.sdk")
+assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
+apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"
diff --git a/study-app-tutorials/apks/app-release.apk b/study-app-tutorials/apks/app-release.apk
new file mode 100644
index 000000000..2ec5e9fee
Binary files /dev/null and b/study-app-tutorials/apks/app-release.apk differ
diff --git a/study-app-tutorials/assets/images/logo128.png b/study-app-tutorials/assets/images/logo128.png
new file mode 100644
index 000000000..bfb3553d2
Binary files /dev/null and b/study-app-tutorials/assets/images/logo128.png differ
diff --git a/study-app-tutorials/assets/images/school-logo.png b/study-app-tutorials/assets/images/school-logo.png
new file mode 100644
index 000000000..0e5b92245
Binary files /dev/null and b/study-app-tutorials/assets/images/school-logo.png differ
diff --git a/study-app-tutorials/assets/images/user_null.png b/study-app-tutorials/assets/images/user_null.png
new file mode 100644
index 000000000..7e620088d
Binary files /dev/null and b/study-app-tutorials/assets/images/user_null.png differ
diff --git a/study-app-tutorials/assets/locale/i18n_en.json b/study-app-tutorials/assets/locale/i18n_en.json
new file mode 100644
index 000000000..e81342527
--- /dev/null
+++ b/study-app-tutorials/assets/locale/i18n_en.json
@@ -0,0 +1,121 @@
+{
+  "all.app.name":"Tutorials ",
+  "all.list.view.no.data":"No data",
+  "all.dialog.info.title":"Info",
+  "all.no.network.tips":"Please make sure connect network",
+  "all.delete.confirm":"Confirm to delete?",
+  "all.save.success":"Success",
+  "all.save.failure":"Failure",
+
+  "main.title":"Welcome ",
+
+  "login.title":"Login ",
+  "login.account.hint":"Mail ",
+  "login.password.hint":"Password ",
+  "login.security.hint":"Security Code ",
+  "login.button":"Go ",
+  "login.password.reset":"Forget Password ",
+  "login.register":"Create New Account ",
+  "login.success.toast":"Login Success ",
+  "login.validation.username":"Username can not be hull or empty ",
+  "login.validation.password":"Password can not be hull or empty ",
+  "login.validation.security":"Security code can not be hull or empty ",
+
+  "forget.password.title":"Forget Password ",
+  "forget.password.mail.hint":"Mail ",
+  "forget.password.security.code.hint":"Security Code ",
+  "forget.password.go":"Next ",
+  "forget.password.security.code.send.hint":"Security Code already Send To  ",
+  "forget.password.reset.confirm":"Reset ",
+  "forget.password.reset.to.login.hint":"Mail Get Password To Login Again",
+  "forget.password.reset.to.login":"Login Again ",
+
+
+  "register.hint":"Register ",
+  "register.mail.hint":"Mail ",
+  "register.password.hint":"Password ",
+  "register.password.again.hint":"Password Again ",
+  "register.security.hint":"Security Code ",
+  "register.next":"Next ",
+  "register.finish.title":"Register Success ",
+  "register.finish.hint":"Login To Get It ",
+  "register.finish.login":"Login ",
+  "register.security.code.send.hint":"Security Code already Send To  ",
+  "register.go":"Register  ",
+
+
+  "home.drawer.title":"Tutorials ",
+  "home.drawer.settings":"Settings ",
+  "home.drawer.about":"About ",
+  "home.drawer.exit":"Exit ",
+  "home.drawer.logout":"Logout ",
+  "home.drawer.profile":"Profile ",
+  "home.back.confirm":"Confirm to exit?",
+  "home.logout.confirm":"Confirm to logout?",
+  "home.back.exit.double.tips":"Double click to exit",
+  "home.menu.tv":"TV",
+  "home.menu.family":"Family",
+  "home.menu.profile":"Profile",
+  "home.menu.apks":"Apks",
+
+  "settings.title":"Settings ",
+  "settings.checkForUpdate":"Check For Update",
+  "settings.feedbacks":"Feedbacks",
+  "settings.dir.setting":"Download Directory",
+  "settings.lang.setting":"System Language",
+  "settings.name.card":"Name Card",
+  "settings.download.dir.setting.success":"Download Directory Updated",
+  "settings.alreadyLatestVersion":"This is already the latest version! ",
+  "settings.downloading":"Downloading...",
+  "settings.upgrade.dialog.title":"Do upgrade to the latest version? ",
+  "settings.upgrade.cancel":"Cancel downloading? ",
+  "settings.upgrade.open.by.browser":"Oepn with browser ",
+  "settings.upgrade.open.by.app":"Download in App",
+  "settings.upgrade.open.cancel":"Cancel ",
+  "settings.lang.zh":"zh-CN ",
+  "settings.lang.en":"en-US ",
+  "settings.lang.cancel":"Cancel ",
+
+  "settings.devTool.title":"Developer Tool",
+
+  "settings.feedback.input.hint":"Feedback",
+  "settings.feedback.all":"All",
+  "settings.feedback.my":"My",
+
+  "tv.list.title":"TV List",
+  "tv.list.bottomAll":"All",
+  "tv.list.bottomFavorite":"Favorite",
+
+  "about.title":"About",
+  "about.versions":"Versions",
+  "about.device.id":"Device ID",
+  "about.author":"About Author",
+  "about.share":"Share APP",
+
+  "about.version.start":"Start",
+  "about.version.name":"Version",
+
+  "about.author.title":"About Author",
+  "about.author.name":"Name",
+
+  "profile.title":"Me",
+
+  "env.title":"Environment Setting",
+  "env.dev":"Development",
+  "env.test":"Test",
+  "env.prod":"Production",
+
+  "env.edit.title":"Environment Edit",
+  "env.edit.protocol":"Protocol",
+  "env.edit.host":"Host",
+  "env.edit.port":"Port",
+
+  "component.http.no.network":"check network state please",
+  "component.crop.title":"Crop",
+
+
+  "name.card.title":"Name Card",
+
+
+  "unknown": ""
+}
\ No newline at end of file
diff --git a/study-app-tutorials/assets/locale/i18n_zh.json b/study-app-tutorials/assets/locale/i18n_zh.json
new file mode 100644
index 000000000..c8d7ed525
--- /dev/null
+++ b/study-app-tutorials/assets/locale/i18n_zh.json
@@ -0,0 +1,118 @@
+{
+  "all.app.name":"小陆 ",
+  "all.list.view.no.data":"暂无数据",
+  "all.dialog.info.title":"提示",
+  "all.no.network.tips":"请先连接网络",
+  "all.delete.confirm":"确认删除?",
+  "all.save.success":"保存成功",
+  "all.save.failure":"保存失败",
+
+  "main.title":"欢迎使用 ",
+
+  "login.title":"用户登陆 ",
+  "login.account.hint":"请输入账号 ",
+  "login.password.hint":"请输入密码 ",
+  "login.security.hint":"请输入验证码 ",
+  "login.button":"立即登陆 ",
+  "login.password.reset":"忘记密码 ",
+  "login.register":"创建账号 ",
+  "login.success.toast":"登陆成功 ",
+  "login.validation.username":"用户名不能为空 ",
+  "login.validation.password":"密码不能为空 ",
+  "login.validation.security":"验证码不能为空 ",
+
+  "forget.password.title":"密码重置 ",
+  "forget.password.mail.hint":"请输入邮箱 ",
+  "forget.password.security.code.hint":"请出入验证码 ",
+  "forget.password.go":"下一步 ",
+  "forget.password.security.code.send.hint":"验证码已发送到 ",
+  "forget.password.reset.confirm":"确认重置 ",
+  "forget.password.reset.finish.hint":"重置成功 ",
+  "forget.password.reset.to.login.hint":"请查看邮箱获取最新密码,重新登陆 ",
+  "forget.password.reset.to.login":"重新登陆 ",
+
+  "register.hint":"用户注册 ",
+  "register.mail.hint":"请输入邮箱 ",
+  "register.password.hint":"请输入密码 ",
+  "register.password.again.hint":"请再次输入密码 ",
+  "register.security.hint":"请输入验证码 ",
+  "register.next":"下一步 ",
+  "register.finish.title":"注册成功 ",
+  "register.finish.hint":"点击登陆,开启新篇章吧 ",
+  "register.finish.login":"登陆 ",
+  "register.security.code.send.hint":"验证码已发送到  ",
+  "register.go":"立即注册  ",
+
+  "home.drawer.title":"小陆 ",
+  "home.drawer.settings":"设置 ",
+  "home.drawer.about":"关于 ",
+  "home.drawer.exit":"退出 ",
+  "home.drawer.logout":"注销 ",
+  "home.drawer.profile":"我的 ",
+  "home.back.confirm":"你确定要退出吗?",
+  "home.logout.confirm":"你确定要注销吗?",
+  "home.back.exit.double.tips":"再点击一次退出应用",
+  "home.menu.tv":"电视",
+  "home.menu.family":"家族",
+  "home.menu.profile":"我的",
+  "home.menu.apks":"应用",
+
+  "settings.title":"设置 ",
+  "settings.checkForUpdate":"检查更新",
+  "settings.feedbacks":"用户留言",
+  "settings.dir.setting":"下载目录",
+  "settings.lang.setting":"系统语言",
+  "settings.name.card":"我的名片",
+  "settings.download.dir.setting.success":"下载目录更新成功",
+  "settings.alreadyLatestVersion":"当前版本已经是最新版本!",
+  "settings.downloading":"正在下载...",
+  "settings.upgrade.dialog.title":"确认更新? ",
+  "settings.upgrade.cancel":"取消下载? ",
+  "settings.upgrade.open.by.browser":"浏览器打开 ",
+  "settings.upgrade.open.by.app":"应用内下载",
+  "settings.upgrade.open.cancel":"取消 ",
+  "settings.lang.zh":"中文 ",
+  "settings.lang.en":"英文 ",
+  "settings.lang.cancel":"取消 ",
+
+  "settings.devTool.title":"开发者工具",
+
+  "settings.feedback.input.hint":"请输入留言内容",
+  "settings.feedback.all":"所有留言",
+  "settings.feedback.my":"我的留言",
+
+  "tv.list.title":"电视列表",
+  "tv.list.bottomAll":"全部",
+  "tv.list.bottomFavorite":"喜欢",
+
+  "about.title":"关于",
+  "about.versions":"版本历史",
+  "about.device.id":"设备编号",
+  "about.author":"关于作者",
+  "about.share":"分享应用",
+
+  "about.version.start":"开始",
+  "about.version.name":"版本",
+
+  "about.author.title":"关于作者",
+  "about.author.name":"名字",
+
+  "profile.title":"我的",
+
+  "env.title":"环境设置",
+  "env.dev":"开发环境",
+  "env.test":"测试环境",
+  "env.prod":"生产环境",
+
+  "env.edit.title":"环境编辑",
+  "env.edit.protocol":"协议",
+  "env.edit.host":"域名",
+  "env.edit.port":"端口",
+
+  "component.http.no.network":"请检查网络是否已经连接",
+  "component.crop.title":"裁剪",
+
+  "name.card.title":"我的名片",
+
+  "unknown": ""
+}
\ No newline at end of file
diff --git a/study-app-tutorials/ios/.gitignore b/study-app-tutorials/ios/.gitignore
new file mode 100644
index 000000000..7a7f9873a
--- /dev/null
+++ b/study-app-tutorials/ios/.gitignore
@@ -0,0 +1,34 @@
+**/dgph
+*.mode1v3
+*.mode2v3
+*.moved-aside
+*.pbxuser
+*.perspectivev3
+**/*sync/
+.sconsign.dblite
+.tags*
+**/.vagrant/
+**/DerivedData/
+Icon?
+**/Pods/
+**/.symlinks/
+profile
+xcuserdata
+**/.generated/
+Flutter/App.framework
+Flutter/Flutter.framework
+Flutter/Flutter.podspec
+Flutter/Generated.xcconfig
+Flutter/ephemeral/
+Flutter/app.flx
+Flutter/app.zip
+Flutter/flutter_assets/
+Flutter/flutter_export_environment.sh
+ServiceDefinitions.json
+Runner/GeneratedPluginRegistrant.*
+
+# Exceptions to above rules.
+!default.mode1v3
+!default.mode2v3
+!default.pbxuser
+!default.perspectivev3
diff --git a/study-app-tutorials/ios/Flutter/AppFrameworkInfo.plist b/study-app-tutorials/ios/Flutter/AppFrameworkInfo.plist
new file mode 100644
index 000000000..8d4492f97
--- /dev/null
+++ b/study-app-tutorials/ios/Flutter/AppFrameworkInfo.plist
@@ -0,0 +1,26 @@
+
+
+
+
+  CFBundleDevelopmentRegion
+  en
+  CFBundleExecutable
+  App
+  CFBundleIdentifier
+  io.flutter.flutter.app
+  CFBundleInfoDictionaryVersion
+  6.0
+  CFBundleName
+  App
+  CFBundlePackageType
+  FMWK
+  CFBundleShortVersionString
+  1.0
+  CFBundleSignature
+  ????
+  CFBundleVersion
+  1.0
+  MinimumOSVersion
+  9.0
+
+
diff --git a/study-app-tutorials/ios/Flutter/Debug.xcconfig b/study-app-tutorials/ios/Flutter/Debug.xcconfig
new file mode 100644
index 000000000..592ceee85
--- /dev/null
+++ b/study-app-tutorials/ios/Flutter/Debug.xcconfig
@@ -0,0 +1 @@
+#include "Generated.xcconfig"
diff --git a/study-app-tutorials/ios/Flutter/Release.xcconfig b/study-app-tutorials/ios/Flutter/Release.xcconfig
new file mode 100644
index 000000000..592ceee85
--- /dev/null
+++ b/study-app-tutorials/ios/Flutter/Release.xcconfig
@@ -0,0 +1 @@
+#include "Generated.xcconfig"
diff --git a/study-app-tutorials/ios/Runner.xcodeproj/project.pbxproj b/study-app-tutorials/ios/Runner.xcodeproj/project.pbxproj
new file mode 100644
index 000000000..7ce3e75c8
--- /dev/null
+++ b/study-app-tutorials/ios/Runner.xcodeproj/project.pbxproj
@@ -0,0 +1,471 @@
+// !$*UTF8*$!
+{
+	archiveVersion = 1;
+	classes = {
+	};
+	objectVersion = 46;
+	objects = {
+
+/* Begin PBXBuildFile section */
+		1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
+		3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
+		74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
+		97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
+		97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
+		97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXCopyFilesBuildPhase section */
+		9705A1C41CF9048500538489 /* Embed Frameworks */ = {
+			isa = PBXCopyFilesBuildPhase;
+			buildActionMask = 2147483647;
+			dstPath = "";
+			dstSubfolderSpec = 10;
+			files = (
+			);
+			name = "Embed Frameworks";
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXCopyFilesBuildPhase section */
+
+/* Begin PBXFileReference section */
+		1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; };
+		1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; };
+		3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; };
+		74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; };
+		74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
+		7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; };
+		9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; };
+		9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; };
+		97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
+		97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
+		97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
+		97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
+		97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+		97C146EB1CF9000F007C117D /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+		9740EEB11CF90186004384FC /* Flutter */ = {
+			isa = PBXGroup;
+			children = (
+				3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
+				9740EEB21CF90195004384FC /* Debug.xcconfig */,
+				7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
+				9740EEB31CF90195004384FC /* Generated.xcconfig */,
+			);
+			name = Flutter;
+			sourceTree = "";
+		};
+		97C146E51CF9000F007C117D = {
+			isa = PBXGroup;
+			children = (
+				9740EEB11CF90186004384FC /* Flutter */,
+				97C146F01CF9000F007C117D /* Runner */,
+				97C146EF1CF9000F007C117D /* Products */,
+			);
+			sourceTree = "";
+		};
+		97C146EF1CF9000F007C117D /* Products */ = {
+			isa = PBXGroup;
+			children = (
+				97C146EE1CF9000F007C117D /* Runner.app */,
+			);
+			name = Products;
+			sourceTree = "";
+		};
+		97C146F01CF9000F007C117D /* Runner */ = {
+			isa = PBXGroup;
+			children = (
+				97C146FA1CF9000F007C117D /* Main.storyboard */,
+				97C146FD1CF9000F007C117D /* Assets.xcassets */,
+				97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
+				97C147021CF9000F007C117D /* Info.plist */,
+				1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
+				1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
+				74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
+				74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
+			);
+			path = Runner;
+			sourceTree = "";
+		};
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+		97C146ED1CF9000F007C117D /* Runner */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
+			buildPhases = (
+				9740EEB61CF901F6004384FC /* Run Script */,
+				97C146EA1CF9000F007C117D /* Sources */,
+				97C146EB1CF9000F007C117D /* Frameworks */,
+				97C146EC1CF9000F007C117D /* Resources */,
+				9705A1C41CF9048500538489 /* Embed Frameworks */,
+				3B06AD1E1E4923F5004D2608 /* Thin Binary */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+			);
+			name = Runner;
+			productName = Runner;
+			productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
+			productType = "com.apple.product-type.application";
+		};
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+		97C146E61CF9000F007C117D /* Project object */ = {
+			isa = PBXProject;
+			attributes = {
+				LastUpgradeCheck = 1020;
+				ORGANIZATIONNAME = "";
+				TargetAttributes = {
+					97C146ED1CF9000F007C117D = {
+						CreatedOnToolsVersion = 7.3.1;
+						LastSwiftMigration = 1100;
+					};
+				};
+			};
+			buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
+			compatibilityVersion = "Xcode 9.3";
+			developmentRegion = en;
+			hasScannedForEncodings = 0;
+			knownRegions = (
+				en,
+				Base,
+			);
+			mainGroup = 97C146E51CF9000F007C117D;
+			productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
+			projectDirPath = "";
+			projectRoot = "";
+			targets = (
+				97C146ED1CF9000F007C117D /* Runner */,
+			);
+		};
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+		97C146EC1CF9000F007C117D /* Resources */ = {
+			isa = PBXResourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
+				3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
+				97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
+				97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXShellScriptBuildPhase section */
+		3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputPaths = (
+			);
+			name = "Thin Binary";
+			outputPaths = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
+		};
+		9740EEB61CF901F6004384FC /* Run Script */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputPaths = (
+			);
+			name = "Run Script";
+			outputPaths = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
+		};
+/* End PBXShellScriptBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+		97C146EA1CF9000F007C117D /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
+				1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXVariantGroup section */
+		97C146FA1CF9000F007C117D /* Main.storyboard */ = {
+			isa = PBXVariantGroup;
+			children = (
+				97C146FB1CF9000F007C117D /* Base */,
+			);
+			name = Main.storyboard;
+			sourceTree = "";
+		};
+		97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
+			isa = PBXVariantGroup;
+			children = (
+				97C147001CF9000F007C117D /* Base */,
+			);
+			name = LaunchScreen.storyboard;
+			sourceTree = "";
+		};
+/* End PBXVariantGroup section */
+
+/* Begin XCBuildConfiguration section */
+		249021D3217E4FDB00AE95B9 /* Profile */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				CLANG_ANALYZER_NONNULL = YES;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+				CLANG_CXX_LIBRARY = "libc++";
+				CLANG_ENABLE_MODULES = YES;
+				CLANG_ENABLE_OBJC_ARC = YES;
+				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+				CLANG_WARN_BOOL_CONVERSION = YES;
+				CLANG_WARN_COMMA = YES;
+				CLANG_WARN_CONSTANT_CONVERSION = YES;
+				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+				CLANG_WARN_EMPTY_BODY = YES;
+				CLANG_WARN_ENUM_CONVERSION = YES;
+				CLANG_WARN_INFINITE_RECURSION = YES;
+				CLANG_WARN_INT_CONVERSION = YES;
+				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+				CLANG_WARN_STRICT_PROTOTYPES = YES;
+				CLANG_WARN_SUSPICIOUS_MOVE = YES;
+				CLANG_WARN_UNREACHABLE_CODE = YES;
+				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+				COPY_PHASE_STRIP = NO;
+				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+				ENABLE_NS_ASSERTIONS = NO;
+				ENABLE_STRICT_OBJC_MSGSEND = YES;
+				GCC_C_LANGUAGE_STANDARD = gnu99;
+				GCC_NO_COMMON_BLOCKS = YES;
+				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+				GCC_WARN_UNDECLARED_SELECTOR = YES;
+				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+				GCC_WARN_UNUSED_FUNCTION = YES;
+				GCC_WARN_UNUSED_VARIABLE = YES;
+				IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+				MTL_ENABLE_DEBUG_INFO = NO;
+				SDKROOT = iphoneos;
+				SUPPORTED_PLATFORMS = iphoneos;
+				TARGETED_DEVICE_FAMILY = "1,2";
+				VALIDATE_PRODUCT = YES;
+			};
+			name = Profile;
+		};
+		249021D4217E4FDB00AE95B9 /* Profile */ = {
+			isa = XCBuildConfiguration;
+			baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
+			buildSettings = {
+				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+				CLANG_ENABLE_MODULES = YES;
+				CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
+				ENABLE_BITCODE = NO;
+				INFOPLIST_FILE = Runner/Info.plist;
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
+				PRODUCT_BUNDLE_IDENTIFIER = com.bage.tutorials;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
+				SWIFT_VERSION = 5.0;
+				VERSIONING_SYSTEM = "apple-generic";
+			};
+			name = Profile;
+		};
+		97C147031CF9000F007C117D /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				CLANG_ANALYZER_NONNULL = YES;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+				CLANG_CXX_LIBRARY = "libc++";
+				CLANG_ENABLE_MODULES = YES;
+				CLANG_ENABLE_OBJC_ARC = YES;
+				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+				CLANG_WARN_BOOL_CONVERSION = YES;
+				CLANG_WARN_COMMA = YES;
+				CLANG_WARN_CONSTANT_CONVERSION = YES;
+				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+				CLANG_WARN_EMPTY_BODY = YES;
+				CLANG_WARN_ENUM_CONVERSION = YES;
+				CLANG_WARN_INFINITE_RECURSION = YES;
+				CLANG_WARN_INT_CONVERSION = YES;
+				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+				CLANG_WARN_STRICT_PROTOTYPES = YES;
+				CLANG_WARN_SUSPICIOUS_MOVE = YES;
+				CLANG_WARN_UNREACHABLE_CODE = YES;
+				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+				COPY_PHASE_STRIP = NO;
+				DEBUG_INFORMATION_FORMAT = dwarf;
+				ENABLE_STRICT_OBJC_MSGSEND = YES;
+				ENABLE_TESTABILITY = YES;
+				GCC_C_LANGUAGE_STANDARD = gnu99;
+				GCC_DYNAMIC_NO_PIC = NO;
+				GCC_NO_COMMON_BLOCKS = YES;
+				GCC_OPTIMIZATION_LEVEL = 0;
+				GCC_PREPROCESSOR_DEFINITIONS = (
+					"DEBUG=1",
+					"$(inherited)",
+				);
+				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+				GCC_WARN_UNDECLARED_SELECTOR = YES;
+				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+				GCC_WARN_UNUSED_FUNCTION = YES;
+				GCC_WARN_UNUSED_VARIABLE = YES;
+				IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+				MTL_ENABLE_DEBUG_INFO = YES;
+				ONLY_ACTIVE_ARCH = YES;
+				SDKROOT = iphoneos;
+				TARGETED_DEVICE_FAMILY = "1,2";
+			};
+			name = Debug;
+		};
+		97C147041CF9000F007C117D /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				CLANG_ANALYZER_NONNULL = YES;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+				CLANG_CXX_LIBRARY = "libc++";
+				CLANG_ENABLE_MODULES = YES;
+				CLANG_ENABLE_OBJC_ARC = YES;
+				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+				CLANG_WARN_BOOL_CONVERSION = YES;
+				CLANG_WARN_COMMA = YES;
+				CLANG_WARN_CONSTANT_CONVERSION = YES;
+				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+				CLANG_WARN_EMPTY_BODY = YES;
+				CLANG_WARN_ENUM_CONVERSION = YES;
+				CLANG_WARN_INFINITE_RECURSION = YES;
+				CLANG_WARN_INT_CONVERSION = YES;
+				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+				CLANG_WARN_STRICT_PROTOTYPES = YES;
+				CLANG_WARN_SUSPICIOUS_MOVE = YES;
+				CLANG_WARN_UNREACHABLE_CODE = YES;
+				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+				COPY_PHASE_STRIP = NO;
+				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+				ENABLE_NS_ASSERTIONS = NO;
+				ENABLE_STRICT_OBJC_MSGSEND = YES;
+				GCC_C_LANGUAGE_STANDARD = gnu99;
+				GCC_NO_COMMON_BLOCKS = YES;
+				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+				GCC_WARN_UNDECLARED_SELECTOR = YES;
+				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+				GCC_WARN_UNUSED_FUNCTION = YES;
+				GCC_WARN_UNUSED_VARIABLE = YES;
+				IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+				MTL_ENABLE_DEBUG_INFO = NO;
+				SDKROOT = iphoneos;
+				SUPPORTED_PLATFORMS = iphoneos;
+				SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
+				TARGETED_DEVICE_FAMILY = "1,2";
+				VALIDATE_PRODUCT = YES;
+			};
+			name = Release;
+		};
+		97C147061CF9000F007C117D /* Debug */ = {
+			isa = XCBuildConfiguration;
+			baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
+			buildSettings = {
+				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+				CLANG_ENABLE_MODULES = YES;
+				CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
+				ENABLE_BITCODE = NO;
+				INFOPLIST_FILE = Runner/Info.plist;
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
+				PRODUCT_BUNDLE_IDENTIFIER = com.bage.tutorials;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
+				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+				SWIFT_VERSION = 5.0;
+				VERSIONING_SYSTEM = "apple-generic";
+			};
+			name = Debug;
+		};
+		97C147071CF9000F007C117D /* Release */ = {
+			isa = XCBuildConfiguration;
+			baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
+			buildSettings = {
+				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+				CLANG_ENABLE_MODULES = YES;
+				CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
+				ENABLE_BITCODE = NO;
+				INFOPLIST_FILE = Runner/Info.plist;
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
+				PRODUCT_BUNDLE_IDENTIFIER = com.bage.tutorials;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
+				SWIFT_VERSION = 5.0;
+				VERSIONING_SYSTEM = "apple-generic";
+			};
+			name = Release;
+		};
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+		97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				97C147031CF9000F007C117D /* Debug */,
+				97C147041CF9000F007C117D /* Release */,
+				249021D3217E4FDB00AE95B9 /* Profile */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+		97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				97C147061CF9000F007C117D /* Debug */,
+				97C147071CF9000F007C117D /* Release */,
+				249021D4217E4FDB00AE95B9 /* Profile */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+/* End XCConfigurationList section */
+	};
+	rootObject = 97C146E61CF9000F007C117D /* Project object */;
+}
diff --git a/study-app-tutorials/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/study-app-tutorials/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 000000000..919434a62
--- /dev/null
+++ b/study-app-tutorials/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+
+
+   
+   
+
diff --git a/study-app-tutorials/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/study-app-tutorials/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 000000000..18d981003
--- /dev/null
+++ b/study-app-tutorials/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+
+
+
+
+	IDEDidComputeMac32BitWarning
+	
+
+
diff --git a/study-app-tutorials/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/study-app-tutorials/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
new file mode 100644
index 000000000..f9b0d7c5e
--- /dev/null
+++ b/study-app-tutorials/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
@@ -0,0 +1,8 @@
+
+
+
+
+	PreviewsEnabled
+	
+
+
diff --git a/study-app-tutorials/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/study-app-tutorials/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
new file mode 100644
index 000000000..a28140cfd
--- /dev/null
+++ b/study-app-tutorials/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
@@ -0,0 +1,91 @@
+
+
+   
+      
+         
+            
+            
+         
+      
+   
+   
+      
+      
+      
+         
+         
+      
+      
+      
+   
+   
+      
+         
+         
+      
+      
+      
+   
+   
+      
+         
+         
+      
+   
+   
+   
+   
+   
+
diff --git a/study-app-tutorials/ios/Runner.xcworkspace/contents.xcworkspacedata b/study-app-tutorials/ios/Runner.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 000000000..1d526a16e
--- /dev/null
+++ b/study-app-tutorials/ios/Runner.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+
+
+   
+   
+
diff --git a/study-app-tutorials/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/study-app-tutorials/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 000000000..18d981003
--- /dev/null
+++ b/study-app-tutorials/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+
+
+
+
+	IDEDidComputeMac32BitWarning
+	
+
+
diff --git a/study-app-tutorials/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/study-app-tutorials/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
new file mode 100644
index 000000000..f9b0d7c5e
--- /dev/null
+++ b/study-app-tutorials/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
@@ -0,0 +1,8 @@
+
+
+
+
+	PreviewsEnabled
+	
+
+
diff --git a/study-app-tutorials/ios/Runner/AppDelegate.swift b/study-app-tutorials/ios/Runner/AppDelegate.swift
new file mode 100644
index 000000000..70693e4a8
--- /dev/null
+++ b/study-app-tutorials/ios/Runner/AppDelegate.swift
@@ -0,0 +1,13 @@
+import UIKit
+import Flutter
+
+@UIApplicationMain
+@objc class AppDelegate: FlutterAppDelegate {
+  override func application(
+    _ application: UIApplication,
+    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
+  ) -> Bool {
+    GeneratedPluginRegistrant.register(with: self)
+    return super.application(application, didFinishLaunchingWithOptions: launchOptions)
+  }
+}
diff --git a/study-app-tutorials/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/study-app-tutorials/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 000000000..d36b1fab2
--- /dev/null
+++ b/study-app-tutorials/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,122 @@
+{
+  "images" : [
+    {
+      "size" : "20x20",
+      "idiom" : "iphone",
+      "filename" : "Icon-App-20x20@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "size" : "20x20",
+      "idiom" : "iphone",
+      "filename" : "Icon-App-20x20@3x.png",
+      "scale" : "3x"
+    },
+    {
+      "size" : "29x29",
+      "idiom" : "iphone",
+      "filename" : "Icon-App-29x29@1x.png",
+      "scale" : "1x"
+    },
+    {
+      "size" : "29x29",
+      "idiom" : "iphone",
+      "filename" : "Icon-App-29x29@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "size" : "29x29",
+      "idiom" : "iphone",
+      "filename" : "Icon-App-29x29@3x.png",
+      "scale" : "3x"
+    },
+    {
+      "size" : "40x40",
+      "idiom" : "iphone",
+      "filename" : "Icon-App-40x40@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "size" : "40x40",
+      "idiom" : "iphone",
+      "filename" : "Icon-App-40x40@3x.png",
+      "scale" : "3x"
+    },
+    {
+      "size" : "60x60",
+      "idiom" : "iphone",
+      "filename" : "Icon-App-60x60@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "size" : "60x60",
+      "idiom" : "iphone",
+      "filename" : "Icon-App-60x60@3x.png",
+      "scale" : "3x"
+    },
+    {
+      "size" : "20x20",
+      "idiom" : "ipad",
+      "filename" : "Icon-App-20x20@1x.png",
+      "scale" : "1x"
+    },
+    {
+      "size" : "20x20",
+      "idiom" : "ipad",
+      "filename" : "Icon-App-20x20@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "size" : "29x29",
+      "idiom" : "ipad",
+      "filename" : "Icon-App-29x29@1x.png",
+      "scale" : "1x"
+    },
+    {
+      "size" : "29x29",
+      "idiom" : "ipad",
+      "filename" : "Icon-App-29x29@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "size" : "40x40",
+      "idiom" : "ipad",
+      "filename" : "Icon-App-40x40@1x.png",
+      "scale" : "1x"
+    },
+    {
+      "size" : "40x40",
+      "idiom" : "ipad",
+      "filename" : "Icon-App-40x40@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "size" : "76x76",
+      "idiom" : "ipad",
+      "filename" : "Icon-App-76x76@1x.png",
+      "scale" : "1x"
+    },
+    {
+      "size" : "76x76",
+      "idiom" : "ipad",
+      "filename" : "Icon-App-76x76@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "size" : "83.5x83.5",
+      "idiom" : "ipad",
+      "filename" : "Icon-App-83.5x83.5@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "size" : "1024x1024",
+      "idiom" : "ios-marketing",
+      "filename" : "Icon-App-1024x1024@1x.png",
+      "scale" : "1x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  }
+}
diff --git a/study-app-tutorials/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/study-app-tutorials/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png
new file mode 100644
index 000000000..dc9ada472
Binary files /dev/null and b/study-app-tutorials/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png differ
diff --git a/study-app-tutorials/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/study-app-tutorials/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png
new file mode 100644
index 000000000..28c6bf030
Binary files /dev/null and b/study-app-tutorials/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png differ
diff --git a/study-app-tutorials/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/study-app-tutorials/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png
new file mode 100644
index 000000000..2ccbfd967
Binary files /dev/null and b/study-app-tutorials/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png differ
diff --git a/study-app-tutorials/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/study-app-tutorials/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png
new file mode 100644
index 000000000..f091b6b0b
Binary files /dev/null and b/study-app-tutorials/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png differ
diff --git a/study-app-tutorials/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/study-app-tutorials/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png
new file mode 100644
index 000000000..4cde12118
Binary files /dev/null and b/study-app-tutorials/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png differ
diff --git a/study-app-tutorials/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/study-app-tutorials/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png
new file mode 100644
index 000000000..d0ef06e7e
Binary files /dev/null and b/study-app-tutorials/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png differ
diff --git a/study-app-tutorials/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/study-app-tutorials/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png
new file mode 100644
index 000000000..dcdc2306c
Binary files /dev/null and b/study-app-tutorials/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png differ
diff --git a/study-app-tutorials/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/study-app-tutorials/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png
new file mode 100644
index 000000000..2ccbfd967
Binary files /dev/null and b/study-app-tutorials/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png differ
diff --git a/study-app-tutorials/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/study-app-tutorials/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png
new file mode 100644
index 000000000..c8f9ed8f5
Binary files /dev/null and b/study-app-tutorials/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png differ
diff --git a/study-app-tutorials/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/study-app-tutorials/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png
new file mode 100644
index 000000000..a6d6b8609
Binary files /dev/null and b/study-app-tutorials/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png differ
diff --git a/study-app-tutorials/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/study-app-tutorials/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png
new file mode 100644
index 000000000..a6d6b8609
Binary files /dev/null and b/study-app-tutorials/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png differ
diff --git a/study-app-tutorials/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/study-app-tutorials/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png
new file mode 100644
index 000000000..75b2d164a
Binary files /dev/null and b/study-app-tutorials/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png differ
diff --git a/study-app-tutorials/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/study-app-tutorials/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png
new file mode 100644
index 000000000..c4df70d39
Binary files /dev/null and b/study-app-tutorials/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png differ
diff --git a/study-app-tutorials/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/study-app-tutorials/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png
new file mode 100644
index 000000000..6a84f41e1
Binary files /dev/null and b/study-app-tutorials/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png differ
diff --git a/study-app-tutorials/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/study-app-tutorials/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png
new file mode 100644
index 000000000..d0e1f5853
Binary files /dev/null and b/study-app-tutorials/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png differ
diff --git a/study-app-tutorials/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/study-app-tutorials/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json
new file mode 100644
index 000000000..0bedcf2fd
--- /dev/null
+++ b/study-app-tutorials/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json
@@ -0,0 +1,23 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "LaunchImage.png",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "LaunchImage@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "filename" : "LaunchImage@3x.png",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  }
+}
diff --git a/study-app-tutorials/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/study-app-tutorials/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
new file mode 100644
index 000000000..9da19eaca
Binary files /dev/null and b/study-app-tutorials/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png differ
diff --git a/study-app-tutorials/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/study-app-tutorials/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
new file mode 100644
index 000000000..9da19eaca
Binary files /dev/null and b/study-app-tutorials/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png differ
diff --git a/study-app-tutorials/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/study-app-tutorials/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png
new file mode 100644
index 000000000..9da19eaca
Binary files /dev/null and b/study-app-tutorials/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png differ
diff --git a/study-app-tutorials/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/study-app-tutorials/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md
new file mode 100644
index 000000000..89c2725b7
--- /dev/null
+++ b/study-app-tutorials/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md
@@ -0,0 +1,5 @@
+# Launch Screen Assets
+
+You can customize the launch screen with your own desired assets by replacing the image files in this directory.
+
+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.
\ No newline at end of file
diff --git a/study-app-tutorials/ios/Runner/Base.lproj/LaunchScreen.storyboard b/study-app-tutorials/ios/Runner/Base.lproj/LaunchScreen.storyboard
new file mode 100644
index 000000000..f2e259c7c
--- /dev/null
+++ b/study-app-tutorials/ios/Runner/Base.lproj/LaunchScreen.storyboard
@@ -0,0 +1,37 @@
+
+
+    
+        
+        
+    
+    
+        
+        
+            
+                
+                    
+                        
+                        
+                    
+                    
+                        
+                        
+                            
+                            
+                        
+                        
+                        
+                            
+                            
+                        
+                    
+                
+                
+            
+            
+        
+    
+    
+        
+    
+
diff --git a/study-app-tutorials/ios/Runner/Base.lproj/Main.storyboard b/study-app-tutorials/ios/Runner/Base.lproj/Main.storyboard
new file mode 100644
index 000000000..f3c28516f
--- /dev/null
+++ b/study-app-tutorials/ios/Runner/Base.lproj/Main.storyboard
@@ -0,0 +1,26 @@
+
+
+    
+        
+        
+    
+    
+        
+        
+            
+                
+                    
+                        
+                        
+                    
+                    
+                        
+                        
+                        
+                    
+                
+                
+            
+        
+    
+
diff --git a/study-app-tutorials/ios/Runner/Info.plist b/study-app-tutorials/ios/Runner/Info.plist
new file mode 100644
index 000000000..73401620b
--- /dev/null
+++ b/study-app-tutorials/ios/Runner/Info.plist
@@ -0,0 +1,45 @@
+
+
+
+
+	CFBundleDevelopmentRegion
+	$(DEVELOPMENT_LANGUAGE)
+	CFBundleExecutable
+	$(EXECUTABLE_NAME)
+	CFBundleIdentifier
+	$(PRODUCT_BUNDLE_IDENTIFIER)
+	CFBundleInfoDictionaryVersion
+	6.0
+	CFBundleName
+	tutorials
+	CFBundlePackageType
+	APPL
+	CFBundleShortVersionString
+	$(FLUTTER_BUILD_NAME)
+	CFBundleSignature
+	????
+	CFBundleVersion
+	$(FLUTTER_BUILD_NUMBER)
+	LSRequiresIPhoneOS
+	
+	UILaunchStoryboardName
+	LaunchScreen
+	UIMainStoryboardFile
+	Main
+	UISupportedInterfaceOrientations
+	
+		UIInterfaceOrientationPortrait
+		UIInterfaceOrientationLandscapeLeft
+		UIInterfaceOrientationLandscapeRight
+	
+	UISupportedInterfaceOrientations~ipad
+	
+		UIInterfaceOrientationPortrait
+		UIInterfaceOrientationPortraitUpsideDown
+		UIInterfaceOrientationLandscapeLeft
+		UIInterfaceOrientationLandscapeRight
+	
+	UIViewControllerBasedStatusBarAppearance
+	
+
+
diff --git a/study-app-tutorials/ios/Runner/Runner-Bridging-Header.h b/study-app-tutorials/ios/Runner/Runner-Bridging-Header.h
new file mode 100644
index 000000000..308a2a560
--- /dev/null
+++ b/study-app-tutorials/ios/Runner/Runner-Bridging-Header.h
@@ -0,0 +1 @@
+#import "GeneratedPluginRegistrant.h"
diff --git a/study-app-tutorials/lib/component/cache/http_request_caches.dart b/study-app-tutorials/lib/component/cache/http_request_caches.dart
new file mode 100644
index 000000000..ab26e5e36
--- /dev/null
+++ b/study-app-tutorials/lib/component/cache/http_request_caches.dart
@@ -0,0 +1,88 @@
+import 'package:tutorials/component/sp/shared_preference_helper.dart';
+import 'package:tutorials/constant/sp_constant.dart';
+import 'package:tutorials/model/request_host.dart';
+
+class HttpRequestCaches {
+  static List caches = [
+    RequestHost(),
+    RequestHost(),
+    RequestHost(),
+    RequestHost()
+  ];
+  static String _index = "1";
+
+  static void inits() async {
+    init("1");
+    init("2");
+    init("3");
+    _index = await SharedPreferenceHelper.get(SpConstant.environment_index, "1");
+  }
+
+  static void init(String index) async {
+    String _protocol = await SharedPreferenceHelper.get(
+        _buildKey(SpConstant.protocol_key, index),
+        SpConstant.protocol_value_default);
+    String _host = await SharedPreferenceHelper.get(
+        _buildKey(SpConstant.host_key, index), SpConstant.host_value_default);
+    String _port = await SharedPreferenceHelper.get(
+        _buildKey(SpConstant.port_key, index), SpConstant.port_value_default);
+    caches[int.parse(index)].protocol = _protocol;
+    caches[int.parse(index)].host = _host;
+    caches[int.parse(index)].port = _port;
+  }
+
+  static Future setProtocol(String value, String index) async {
+    caches[int.parse(index)].protocol = value;
+    return SharedPreferenceHelper.set(
+        _buildKey(SpConstant.protocol_key, index), value);
+  }
+
+  static Future setHost(String value, String index) async {
+    caches[int.parse(index)].host = value;
+    return SharedPreferenceHelper.set(
+        _buildKey(SpConstant.host_key, index), value);
+  }
+
+  static Future setPort(String value, String index) async {
+    caches[int.parse(index)].port = value;
+    return SharedPreferenceHelper.set(
+        _buildKey(SpConstant.port_key, index), value);
+  }
+
+  static Future setIndex(String index) async{
+    _index = index;
+    return SharedPreferenceHelper.set(SpConstant.environment_index, index);
+  }
+
+  static String getIndex() {
+    return _index;
+  }
+
+  static String getProtocol() {
+    return caches[int.parse(_index)].protocol;
+  }
+
+  static String getIndexProtocol(String index) {
+    return caches[int.parse(index)].protocol;
+  }
+
+  static String getHost() {
+    return caches[int.parse(_index)].host;
+  }
+
+  static String getIndexHost(String index) {
+    return caches[int.parse(index)].host;
+  }
+
+  static String getPort() {
+    return caches[int.parse(_index)].port;
+  }
+
+  static String getIndexPort(String index) {
+    return caches[int.parse(index)].port;
+  }
+
+  static String _buildKey(String key, String index) {
+    return "http_request_" + index + "_" + key;
+  }
+}
diff --git a/study-app-tutorials/lib/component/cache/setting_caches.dart b/study-app-tutorials/lib/component/cache/setting_caches.dart
new file mode 100644
index 000000000..72529a17a
--- /dev/null
+++ b/study-app-tutorials/lib/component/cache/setting_caches.dart
@@ -0,0 +1,24 @@
+import 'package:tutorials/component/sp/shared_preference_helper.dart';
+import 'package:tutorials/constant/sp_constant.dart';
+
+class SettingCaches {
+  static Future getDownloadDirectory() {
+    return SharedPreferenceHelper.get(SpConstant.download_dir_cache_key,
+        SpConstant.download_dir_value_default);
+  }
+
+  static Future cacheDownloadDirectory(String value) {
+    return SharedPreferenceHelper.set(SpConstant.download_dir_cache_key, value);
+  }
+
+
+  static Future getMockSwitch() {
+    return SharedPreferenceHelper.get(SpConstant.mock_switch_cache_key,
+        SpConstant.mock_switch_value_default);
+  }
+
+
+  static Future cacheMockSwitch(String value) {
+    return SharedPreferenceHelper.set(SpConstant.mock_switch_cache_key, value);
+  }
+}
diff --git a/study-app-tutorials/lib/component/cache/token_caches.dart b/study-app-tutorials/lib/component/cache/token_caches.dart
new file mode 100644
index 000000000..db662d36b
--- /dev/null
+++ b/study-app-tutorials/lib/component/cache/token_caches.dart
@@ -0,0 +1,31 @@
+
+import 'package:tutorials/component/sp/shared_preference_helper.dart';
+import 'package:tutorials/constant/sp_constant.dart';
+
+class TokenCaches{
+
+  static Future getAccessToken(){
+    return SharedPreferenceHelper.get(SpConstant.token_access_key,'');
+  }
+  
+  static Future cacheAccessToken(String accessToken){
+    return SharedPreferenceHelper.set(SpConstant.token_access_key, accessToken);
+  }
+
+  static Future clearAccessToken(){
+    return SharedPreferenceHelper.set(SpConstant.token_access_key, '');
+  }
+
+  static Future getRefreshToken(){
+    return SharedPreferenceHelper.get(SpConstant.token_refresh_key,'');
+  }
+  
+  static Future cacheRefreshToken(String refreshToken){
+    return SharedPreferenceHelper.set(SpConstant.token_refresh_key, refreshToken);
+  }
+
+  static Future clearRefreshToken(){
+    return SharedPreferenceHelper.set(SpConstant.token_refresh_key, '');
+  }
+
+}
\ No newline at end of file
diff --git a/study-app-tutorials/lib/component/cache/user_caches.dart b/study-app-tutorials/lib/component/cache/user_caches.dart
new file mode 100644
index 000000000..36bf77501
--- /dev/null
+++ b/study-app-tutorials/lib/component/cache/user_caches.dart
@@ -0,0 +1,25 @@
+
+import 'package:tutorials/request/model/user.dart';
+
+class UserCaches{
+
+  static int _userId = 0;
+  static User _user = User();
+
+  static int getUserId(){
+    return _userId;
+  }
+
+  static void cacheUserId(int userId){
+    _userId = userId;
+  }
+
+  static void cacheUser(User user){
+    _user = user??_user;
+    _user.id = user.id??_userId;
+  }
+
+  static User getUser(){
+    return _user??User();
+  }
+}
\ No newline at end of file
diff --git a/study-app-tutorials/lib/component/dialog/dialogs.dart b/study-app-tutorials/lib/component/dialog/dialogs.dart
new file mode 100644
index 000000000..6e77ad3a1
--- /dev/null
+++ b/study-app-tutorials/lib/component/dialog/dialogs.dart
@@ -0,0 +1,220 @@
+import 'package:tutorials/locale/translations.dart';
+import 'package:flutter/material.dart';
+
+class Dialogs {
+  static Future showProgress(
+      BuildContext context, String title, double? value, onWillPop) {
+    return showDialog(
+        context: context,
+        barrierDismissible: false, //点击遮罩不关闭对话框
+        builder: (context) {
+          return new WillPopScope(
+              onWillPop: onWillPop, //重点此举
+              child: AlertDialog(
+                content: Column(
+                  mainAxisSize: MainAxisSize.min,
+                  children: [
+                    CircularProgressIndicator(value: value,),
+                    Padding(
+                      padding: const EdgeInsets.only(top: 26.0),
+                      child: Text(title),
+                    )
+                  ],
+                ),
+              ));
+        });
+  }
+
+  static Future dismiss(BuildContext context) async {
+    Navigator.of(context).pop();
+  }
+
+  static Future showInfoDialog(
+      BuildContext context, String? title, String content) {
+    TextEditingController _controller = TextEditingController();
+    return showDialog(
+      context: context,
+      builder: (context) {
+        return AlertDialog(
+          title: Text(
+              title ?? Translations.textOf(context, "all.dialog.info.title")),
+          content: Text(content),
+          actions: [
+            FlatButton(
+              child: Text("确定"),
+              onPressed: () {
+                Navigator.of(context).pop(_controller.text.toString());
+              },
+            ),
+          ],
+        );
+      },
+    );
+  }
+
+  static Future showConfirmDialog(
+      BuildContext context, String title, String? content) {
+    List actions = [
+      FlatButton(
+        child: Text("取消"),
+        onPressed: () => Navigator.of(context).pop(), // 关闭对话框
+      ),
+      FlatButton(
+        child: Text("确定"),
+        onPressed: () {
+          Navigator.of(context).pop("true");
+        },
+      ),
+    ];
+    if (content == null) {
+      return showDialog(
+        context: context,
+        builder: (context) {
+          return AlertDialog(title: Text(title), actions: actions);
+        },
+      );
+    }
+    return showDialog(
+      context: context,
+      builder: (context) {
+        return AlertDialog(
+            title: Text(title), content: Text(content), actions: actions);
+      },
+    );
+  }
+
+  // ref: https://stackoverflow.com/questions/53311553/how-to-set-showmodalbottomsheet-to-full-height
+  static Future showButtonSelectDialog(
+      BuildContext context, List contents, List? icons) {
+    return showModalBottomSheet(
+      isScrollControlled: true,
+      context: context,
+      isDismissible: true,
+      builder: (context) => Wrap(children: [
+        ListView.builder(
+            itemCount: contents.length,
+            shrinkWrap: true,
+            itemBuilder: (BuildContext context, int index) {
+              return icons == null
+                  ? ListTile(
+                      title: Text(contents[index]),
+                      onTap: () => Navigator.of(context).pop(index),
+                    )
+                  : ListTile(
+                      title: Text(contents[index]),
+                      trailing: icons[index],
+                      onTap: () => Navigator.of(context).pop(index),
+                    );
+            })
+      ]),
+    );
+  }
+
+  static Future showInputDialog(
+      BuildContext context, String title, String defaultValue) {
+    TextEditingController _controller = TextEditingController.fromValue(
+        TextEditingValue(
+            text: defaultValue,
+            selection: TextSelection.fromPosition(TextPosition(
+                affinity: TextAffinity.downstream,
+                offset: defaultValue.length))));
+    return showDialog(
+      context: context,
+      builder: (context) {
+        return AlertDialog(
+          title: Text(title),
+          content: TextField(
+            controller: _controller,
+            decoration: InputDecoration(
+              contentPadding: EdgeInsets.all(10.0), //内容内边距
+            ),
+            autofocus: true, //自动获取焦点,设置false
+          ),
+          actions: [
+            FlatButton(
+              child: Text("取消"),
+              onPressed: () => Navigator.of(context).pop(), // 关闭对话框
+            ),
+            FlatButton(
+              child: Text("确定"),
+              onPressed: () {
+                Navigator.of(context).pop(_controller.text.toString());
+              },
+            ),
+          ],
+        );
+      },
+    );
+  }
+
+  static Future showListBottomSheet(
+      BuildContext context, List list) {
+    return showModalBottomSheet(
+      context: context,
+      builder: (BuildContext context) {
+        return ListView.builder(
+          itemCount: list.length,
+          itemBuilder: (BuildContext context, int index) {
+            return ListTile(
+              title: Text(list[index]),
+              onTap: () => Navigator.of(context).pop(index),
+            );
+          },
+        );
+      },
+    );
+  }
+
+  static Future showInputBottomSheet(
+      BuildContext context, String helperText, String defaultValue) {
+    TextEditingController _controller = TextEditingController.fromValue(
+        TextEditingValue(
+            text: defaultValue,
+            selection: TextSelection.fromPosition(TextPosition(
+                affinity: TextAffinity.downstream,
+                offset: defaultValue.length))));
+    return showModalBottomSheet(
+        context: context,
+        shape: RoundedRectangleBorder(
+            borderRadius: BorderRadius.horizontal(
+          left: Radius.circular(8),
+          right: Radius.circular(8),
+        )),
+        builder: (BuildContext context) {
+          return Container(
+            child: Row(
+              children: [
+                Expanded(
+                    child: Column(
+                  crossAxisAlignment: CrossAxisAlignment.start,
+                  children: [
+                    TextField(
+                      controller: _controller,
+                      decoration: InputDecoration(
+                        contentPadding: EdgeInsets.all(10.0),
+                        helperText: helperText,
+                      ),
+                      autofocus: true, //自动获取焦点,设置false
+                    ),
+                  ],
+                )),
+                Column(
+                    crossAxisAlignment: CrossAxisAlignment.end,
+                    children: [
+                      IconButton(
+                          icon: Icon(
+                            Icons.send,
+                            color: Colors.blue,
+                            size: 20.0,
+                          ),
+                          onPressed: () {
+                            Navigator.of(context)
+                                .pop(_controller.text.toString());
+                          }),
+                    ])
+              ],
+            ),
+          );
+        });
+  }
+}
diff --git a/study-app-tutorials/lib/component/dialog/loading.dart b/study-app-tutorials/lib/component/dialog/loading.dart
new file mode 100644
index 000000000..6ef6a109b
--- /dev/null
+++ b/study-app-tutorials/lib/component/dialog/loading.dart
@@ -0,0 +1,18 @@
+import 'package:flutter_easyloading/flutter_easyloading.dart';
+
+/// EasyLoading.show(status: 'loading...');
+// EasyLoading.showProgress(0.3, status: 'downloading...');
+// EasyLoading.showSuccess('Great Success!');
+// EasyLoading.showError('Failed with Error');
+// EasyLoading.showInfo('Useful Information.');
+// EasyLoading.showToast('Toast');
+// EasyLoading.dismiss();
+class Loading {
+  static void showLoading(String title, double value) {
+    EasyLoading.showProgress(value, status: title);
+  }
+
+  static void dismiss() {
+    EasyLoading.dismiss();
+  }
+}
diff --git a/study-app-tutorials/lib/component/dialog/progress_dialogs.dart b/study-app-tutorials/lib/component/dialog/progress_dialogs.dart
new file mode 100644
index 000000000..9c2e45834
--- /dev/null
+++ b/study-app-tutorials/lib/component/dialog/progress_dialogs.dart
@@ -0,0 +1,25 @@
+import 'package:flutter/material.dart';
+import 'package:sn_progress_dialog/sn_progress_dialog.dart' as sn_progress_dialog;
+
+class ProgressDialog {
+
+  late sn_progress_dialog.ProgressDialog _pd;
+
+  ProgressDialog(BuildContext context){
+    _pd = sn_progress_dialog.ProgressDialog(context: context);
+  }
+
+
+  void showProgress(String title, int max) {
+    _pd.show(max: max, msg: title);
+  }
+
+  void updateProgress(int value) {
+    _pd.update(value: value);
+  }
+
+  void dismiss() {
+    _pd.close();
+  }
+
+}
diff --git a/study-app-tutorials/lib/component/downloader/file_downloader.dart b/study-app-tutorials/lib/component/downloader/file_downloader.dart
new file mode 100644
index 000000000..f519affe6
--- /dev/null
+++ b/study-app-tutorials/lib/component/downloader/file_downloader.dart
@@ -0,0 +1,8 @@
+import 'package:flutter_cache_manager/flutter_cache_manager.dart';
+
+class FileDownloader {
+
+  static Stream download(String url) {
+    return DefaultCacheManager().getFileStream(url, withProgress: true);
+  }
+}
diff --git a/study-app-tutorials/lib/component/event/base_event.dart b/study-app-tutorials/lib/component/event/base_event.dart
new file mode 100644
index 000000000..4e53e9c0b
--- /dev/null
+++ b/study-app-tutorials/lib/component/event/base_event.dart
@@ -0,0 +1,3 @@
+
+
+class BaseEvent {}
diff --git a/study-app-tutorials/lib/component/event/event_bus.dart b/study-app-tutorials/lib/component/event/event_bus.dart
new file mode 100644
index 000000000..4db7757de
--- /dev/null
+++ b/study-app-tutorials/lib/component/event/event_bus.dart
@@ -0,0 +1,18 @@
+import 'package:event_bus/event_bus.dart' as marcojakob;
+
+typedef EventCallback = void Function(dynamic event);
+
+class EventBus {
+  static marcojakob.EventBus _eventBus = marcojakob.EventBus();
+
+  static void publish(event) {
+    _eventBus.fire(event);
+  }
+
+  static void consume(EventCallback callback) {
+    _eventBus.on().listen((event) {
+      // All events are of type UserLoggedInEvent (or subtypes of it).
+      callback(event);
+    });
+  }
+}
diff --git a/study-app-tutorials/lib/component/http/cancel_requests.dart b/study-app-tutorials/lib/component/http/cancel_requests.dart
new file mode 100644
index 000000000..0c72d19c9
--- /dev/null
+++ b/study-app-tutorials/lib/component/http/cancel_requests.dart
@@ -0,0 +1,11 @@
+import 'package:dio/dio.dart';
+
+class CancelRequests {
+
+  CancelToken token = CancelToken();
+
+  void cancel([dynamic reason]) {
+    token.cancel(reason);
+  }
+
+}
diff --git a/study-app-tutorials/lib/component/http/http_byte_result.dart b/study-app-tutorials/lib/component/http/http_byte_result.dart
new file mode 100644
index 000000000..a4f435ab1
--- /dev/null
+++ b/study-app-tutorials/lib/component/http/http_byte_result.dart
@@ -0,0 +1,7 @@
+import 'dart:collection';
+
+class HttpByteResult {
+  int statusCode = 0;
+  var responseBytes;
+  Map> headers = HashMap();
+}
diff --git a/study-app-tutorials/lib/component/http/http_origin_result.dart b/study-app-tutorials/lib/component/http/http_origin_result.dart
new file mode 100644
index 000000000..dcc82e1d3
--- /dev/null
+++ b/study-app-tutorials/lib/component/http/http_origin_result.dart
@@ -0,0 +1,49 @@
+class HttpOriginResult {
+  int? code;
+  int? originCode;
+  String? msg;
+  Null? originMsg;
+  Data? data;
+
+  HttpOriginResult(
+      {this.code, this.originCode, this.msg, this.originMsg, this.data});
+
+  HttpOriginResult.fromJson(Map json) {
+    code = json['code'];
+    originCode = json['originCode'];
+    msg = json['msg'];
+    originMsg = json['originMsg'];
+    data = json['data'] != null ? new Data.fromJson(json['data']) : null;
+  }
+
+  Map toJson() {
+    final Map data = new Map();
+    data['code'] = this.code;
+    data['originCode'] = this.originCode;
+    data['msg'] = this.msg;
+    data['originMsg'] = this.originMsg;
+    if (this.data != null) {
+      data['data'] = this.data!.toJson();
+    }
+    return data;
+  }
+}
+
+class Data {
+  String? error;
+  String? errorDescription;
+
+  Data({this.error, this.errorDescription});
+
+  Data.fromJson(Map json) {
+    error = json['error'];
+    errorDescription = json['error_description'];
+  }
+
+  Map toJson() {
+    final Map data = new Map();
+    data['error'] = this.error;
+    data['error_description'] = this.errorDescription;
+    return data;
+  }
+}
diff --git a/study-app-tutorials/lib/component/http/http_progress_callback.dart b/study-app-tutorials/lib/component/http/http_progress_callback.dart
new file mode 100644
index 000000000..9485d20eb
--- /dev/null
+++ b/study-app-tutorials/lib/component/http/http_progress_callback.dart
@@ -0,0 +1,2 @@
+
+typedef HttpProgressCallback = void Function(int count, int total);
diff --git a/study-app-tutorials/lib/component/http/http_requests.dart b/study-app-tutorials/lib/component/http/http_requests.dart
new file mode 100644
index 000000000..babe87cf2
--- /dev/null
+++ b/study-app-tutorials/lib/component/http/http_requests.dart
@@ -0,0 +1,265 @@
+import 'dart:convert';
+
+import 'package:tutorials/component/http/cancel_requests.dart';
+import 'package:tutorials/component/http/http_byte_result.dart';
+import 'package:tutorials/component/http/http_origin_result.dart';
+import 'package:tutorials/component/http/http_progress_callback.dart';
+import 'package:tutorials/component/http/http_result.dart';
+import 'package:tutorials/component/http/network_check_interceptors.dart';
+import 'package:tutorials/component/log/logs.dart';
+import 'package:tutorials/constant/http_constant.dart';
+import 'package:tutorials/prop/http_prop.dart';
+import 'package:dio/dio.dart';
+
+class HttpRequests {
+  static final Dio _dio = Dio();
+
+  static void init() {
+    _dio.interceptors.add(NetworkCheckInterceptors());
+  }
+
+  static Future upload(
+      String path,
+      Map? parameters,
+      List files,
+      Map? headers,
+      HttpProgressCallback onProgress,
+      CancelRequests cancelRequests) async {
+    return _doUploadRequest(
+        path, parameters, files, headers, onProgress, cancelRequests);
+  }
+
+  static Future getBytes(
+      String path,
+      Map? parameters,
+      Map? headers,
+      HttpProgressCallback onProgress,
+      CancelRequests cancelRequests) async {
+    return _doDownloadRequest(
+        path, parameters, null, headers, "get", onProgress, cancelRequests);
+  }
+
+  static Future postBytes(
+      String path,
+      Map parameters,
+      Map headers,
+      HttpProgressCallback onProgress,
+      CancelRequests cancelRequests) async {
+    return _doDownloadRequest(
+        path, parameters, null, headers, "post", onProgress, cancelRequests);
+  }
+
+  static Future get(String path, Map? parameters,
+      Map? headers) async {
+    return _doBaseRequest(path, parameters, null, headers, "get", null);
+  }
+
+  static Future post(String path, Map? parameters,
+      Map? headers) async {
+    return _doBaseRequest(path, parameters, null, headers, "post", null);
+  }
+
+  static Future postRaw(
+      String path, dynamic data, Map headers) async {
+    return _doBaseRequest(path, null, data, headers, "post", null);
+  }
+
+  static Future getWith(
+      String path,
+      Map parameters,
+      Map headers,
+      int timeoutMilliseconds) async {
+    return _doBaseRequest(
+        path, parameters, null, headers, "get", timeoutMilliseconds);
+  }
+
+  static Future postWith(
+      String path,
+      Map parameters,
+      Map headers,
+      int timeoutMilliseconds) async {
+    return _doBaseRequest(
+        path, parameters, null, headers, "post", timeoutMilliseconds);
+  }
+
+  static Future postRawWith(String path, dynamic data,
+      Map headers, int timeoutMilliseconds) async {
+    return _doBaseRequest(
+        path, null, data, headers, "post", timeoutMilliseconds);
+  }
+
+  static Future _doDownloadRequest(
+      String path,
+      Map? parameters,
+      dynamic data,
+      Map? headers,
+      String method,
+      HttpProgressCallback onProgress,
+      CancelRequests cancelRequests) async {
+    HttpByteResult result = HttpByteResult();
+    try {
+      path = rebuildUrl(path);
+      Logs.info('_doDownloadRequest path = ${path}');
+      Logs.info('_doDownloadRequest parameters = ${parameters}');
+      Logs.info('_doDownloadRequest data = ${data}');
+      _dio.options = _buildDownloadOption(parameters, data, headers);
+      Response response;
+      if ("get".compareTo(method) == 0) {
+        response = await _dio.get(path,
+            queryParameters: parameters, cancelToken: cancelRequests.token,
+            onReceiveProgress: (int sent, int total) {
+          Logs.info("_doDownloadRequest onReceiveProgress ${sent} / ${total}");
+          onProgress?.call(sent, total);
+        });
+      } else {
+        response = await _dio.post(path,
+            queryParameters: parameters,
+            cancelToken: cancelRequests.token,
+            data: data, onReceiveProgress: (int sent, int total) {
+          Logs.info("_doDownloadRequest onReceiveProgress ${sent} / ${total}");
+          onProgress?.call(sent, total);
+        });
+      }
+      Logs.info('_doDownloadRequest statusCode = ${response.statusCode}');
+      result.responseBytes = response.data;
+      result.statusCode = response.statusCode ?? 500;
+      result.headers = response.headers.map;
+      return result;
+    } catch (e) {
+      Logs.info('_doDownloadRequest error' + e.toString());
+      result.statusCode = HttpConstant.server_not_response;
+    }
+    return result;
+  }
+
+  static Future _doUploadRequest(
+      String path,
+      Map? parameters,
+      List files,
+      Map? headers,
+      HttpProgressCallback onProgress,
+      CancelRequests cancelRequests) async {
+    HttpByteResult result = HttpByteResult();
+    try {
+      path = rebuildUrl(path);
+      Logs.info('_doUploadRequest path = ${path}');
+      Logs.info('_doUploadRequest parameters = ${parameters}');
+      Logs.info('_doUploadRequest files.length = ${files?.length}');
+
+      Map paramMap = parameters ?? {};
+      paramMap['files'] = files;
+      FormData formData = FormData.fromMap(paramMap);
+
+      _dio.options = BaseOptions(
+          baseUrl: HttpProp.getBaseUrl(),
+          headers: headers,
+          contentType: HttpProp.getContentType(),
+          followRedirects: false,
+          validateStatus: (status) {
+            return (status ?? 500) < 500;
+          });
+      Response response = await _dio.post(path,
+          queryParameters: parameters,
+          cancelToken: cancelRequests.token,
+          data: formData, onReceiveProgress: (int sent, int total) {
+        Logs.info("_doUploadRequest onReceiveProgress ${sent} / ${total}");
+        onProgress?.call(sent, total);
+      });
+      Logs.info('_doUploadRequest statusCode = ${response.statusCode}');
+      result.responseBytes = response.data;
+      result.statusCode = response.statusCode ?? 500;
+      result.headers = response.headers.map;
+      return result;
+    } catch (e) {
+      Logs.info('_doUploadRequest error' + e.toString());
+      result.statusCode = HttpConstant.server_not_response;
+    }
+    return result;
+  }
+
+  static Future _doBaseRequest(
+      String path,
+      Map? parameters,
+      dynamic? data,
+      Map? headers,
+      String method,
+      int? timeoutMilliseconds) async {
+    HttpResult result = HttpResult();
+    try {
+      path = rebuildUrl(path);
+      Logs.info('_doBaseRequest path = ${path}');
+      Logs.info('_doBaseRequest parameters = ${parameters}');
+      Logs.info('_doBaseRequest data = ${data}');
+      _dio.options = _buildOption(
+          parameters ?? {}, data, headers ?? {}, timeoutMilliseconds ?? 6000);
+      Response response;
+      if ("get".compareTo(method) == 0) {
+        response = await _dio.get(path, queryParameters: parameters);
+      } else {
+        response =
+            await _dio.post(path, queryParameters: parameters, data: data);
+      }
+      Logs.info('_doBaseRequest statusCode = ${response.statusCode}');
+      result.responseBody =
+          response.data == null || "".compareTo(response.data) == 0
+              ? "{}"
+              : response.data;
+      result.statusCode = response.statusCode ?? 500;
+      result.headers = response.headers.map;
+      return result;
+    } on DioError catch (e) {
+      Logs.info('_doBaseRequest DioError ${e.toString()}' );
+      Logs.info('_doBaseRequest DioError data ${e.response?.data}');
+      result.statusCode = e.response?.statusCode??HttpConstant.server_not_response;
+      result.responseBody = '{ "msg":"${e.response?.statusMessage??HttpConstant.server_not_response_msg}" }';
+
+      try{
+        if(e.response?.statusCode == 400 && (e?.response?.statusMessage?.isEmpty??true)){
+          HttpOriginResult originResult = HttpOriginResult.fromJson(jsonDecode(e.response?.data));
+          result.responseBody = '{ "msg":"${originResult.data?.errorDescription??HttpConstant.server_not_response_msg}" }';
+        }
+      }catch(e){
+        Logs.info(e.toString());
+      }
+
+    } catch (e) {
+      Logs.info('_doBaseRequest error ${e.toString()}' );
+      result.statusCode = HttpConstant.server_not_response;
+      result.responseBody = '{ "msg":"${HttpConstant.server_not_response_msg}" }';
+    }
+    return result;
+  }
+
+  static BaseOptions _buildDownloadOption(Map? parameters,
+      dynamic data, Map? headers) {
+    return BaseOptions(
+        baseUrl: HttpProp.getBaseUrl(),
+//        receiveTimeout: HttpProp.timeout, // 不需要指定超时时间
+        headers: headers,
+        contentType: HttpProp.getContentType(),
+        responseType: ResponseType.bytes,
+        followRedirects: false,
+        validateStatus: (status) {
+          return (status ?? 500) < 500;
+        });
+  }
+
+  static BaseOptions _buildOption(Map parameters, dynamic data,
+      Map headers, int timeoutMilliseconds) {
+    return BaseOptions(
+      baseUrl: HttpProp.getBaseUrl(),
+      connectTimeout: HttpProp.getTimeout(),
+      receiveTimeout: HttpProp.getTimeout(),
+      headers: headers,
+      contentType: HttpProp.getContentType(),
+      responseType: ResponseType.plain,
+    );
+  }
+
+  static String rebuildUrl(String url) {
+    if (url.startsWith("/") && HttpProp.getBaseUrl().endsWith("/")) {
+      url = url.substring(1);
+    }
+    return url.startsWith("http") ? url : HttpProp.getBaseUrl() + url;
+  }
+}
diff --git a/study-app-tutorials/lib/component/http/http_result.dart b/study-app-tutorials/lib/component/http/http_result.dart
new file mode 100644
index 000000000..970d3bd51
--- /dev/null
+++ b/study-app-tutorials/lib/component/http/http_result.dart
@@ -0,0 +1,7 @@
+import 'dart:collection';
+
+class HttpResult {
+  int statusCode = 0;
+  String responseBody = "";
+  Map> headers = HashMap();
+}
diff --git a/study-app-tutorials/lib/component/http/network_check_interceptors.dart b/study-app-tutorials/lib/component/http/network_check_interceptors.dart
new file mode 100644
index 000000000..e70c66f20
--- /dev/null
+++ b/study-app-tutorials/lib/component/http/network_check_interceptors.dart
@@ -0,0 +1,31 @@
+import 'package:tutorials/component/toast/Toasts.dart';
+import 'package:tutorials/utils/app_utils.dart';
+import 'package:dio/dio.dart';
+
+class NetworkCheckInterceptors implements InterceptorsWrapper {
+
+  NetworkCheckInterceptors(){
+  }
+
+
+  @override
+  void onError(DioError err, ErrorInterceptorHandler handler) {
+    AppUtils.isConnected().then((value) => {
+      if(!value){
+        Toasts.show('请先连接网络')
+      }
+    });
+    handler.next(err); //continue
+  }
+
+  @override
+  void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
+    handler.next(options); //continue
+  }
+
+  @override
+  void onResponse(
+      Response response, ResponseInterceptorHandler handler) {
+    handler.next(response); //continue
+  }
+}
diff --git a/study-app-tutorials/lib/component/image.crop/image_cropper.dart b/study-app-tutorials/lib/component/image.crop/image_cropper.dart
new file mode 100644
index 000000000..bf17f1b08
--- /dev/null
+++ b/study-app-tutorials/lib/component/image.crop/image_cropper.dart
@@ -0,0 +1,30 @@
+import 'package:flutter/material.dart';
+import 'package:image_cropper/image_cropper.dart' as origin_image_cropper;
+import 'package:tutorials/locale/translations.dart';
+
+class ImageCropper {
+  static Future cropImage(
+      BuildContext context, String valueCrop) async {
+    String _title = Translations.textOf(context, "component.crop.title");
+
+    final croppedFile = await origin_image_cropper.ImageCropper().cropImage(
+      sourcePath: valueCrop,
+      compressFormat: origin_image_cropper.ImageCompressFormat.jpg,
+      compressQuality: 100,
+      androidUiSettings: origin_image_cropper.AndroidUiSettings(
+          toolbarTitle: _title,
+          toolbarColor: Colors.blue,
+          toolbarWidgetColor: Colors.white,
+          initAspectRatio: origin_image_cropper.CropAspectRatioPreset.original,
+          hideBottomControls: true,
+          lockAspectRatio: false),
+      iosUiSettings: origin_image_cropper.IOSUiSettings(
+        title: _title,
+      ),
+    );
+    if (croppedFile != null) {
+      return croppedFile.path;
+    }
+    return valueCrop;
+  }
+}
diff --git a/study-app-tutorials/lib/component/log/logs.dart b/study-app-tutorials/lib/component/log/logs.dart
new file mode 100644
index 000000000..a9cbab6a1
--- /dev/null
+++ b/study-app-tutorials/lib/component/log/logs.dart
@@ -0,0 +1,9 @@
+class Logs {
+
+  /**
+   * 打印日志
+   */
+  static void info(String msg) {
+    print(msg);
+  }
+}
diff --git a/study-app-tutorials/lib/component/permission/permission_helper.dart b/study-app-tutorials/lib/component/permission/permission_helper.dart
new file mode 100644
index 000000000..710589929
--- /dev/null
+++ b/study-app-tutorials/lib/component/permission/permission_helper.dart
@@ -0,0 +1,21 @@
+import 'package:tutorials/component/log/logs.dart';
+import 'package:permission_handler/permission_handler.dart';
+
+class PermissionHelper {
+  static Future requestPermissions() async {
+    Map statuses =
+        await [Permission.storage,Permission.activityRecognition].request();
+    bool result = true;
+    statuses.forEach((key, value) {
+      if (!value.isGranted) {
+        Logs.info('${key} is not Granted!!');
+        result = false;
+      }
+    });
+    return result;
+  }
+
+  static Future _requestPermission(Permission permission) async {
+    return permission.request().isGranted;
+  }
+}
diff --git a/study-app-tutorials/lib/component/picker/file_picker.dart b/study-app-tutorials/lib/component/picker/file_picker.dart
new file mode 100644
index 000000000..074d00df6
--- /dev/null
+++ b/study-app-tutorials/lib/component/picker/file_picker.dart
@@ -0,0 +1,10 @@
+import 'dart:io';
+
+import 'package:file_picker/file_picker.dart' as origin_file_picker;
+
+/// https://pub.flutter-io.cn/packages/file_picker
+class FilePicker {
+  static Future pickDirectory() async {
+    return await origin_file_picker.FilePicker.platform.getDirectoryPath();
+  }
+}
diff --git a/study-app-tutorials/lib/component/picker/image_picker.dart b/study-app-tutorials/lib/component/picker/image_picker.dart
new file mode 100644
index 000000000..b91fbd11d
--- /dev/null
+++ b/study-app-tutorials/lib/component/picker/image_picker.dart
@@ -0,0 +1,23 @@
+import 'package:image_picker/image_picker.dart' as origin_image_picker;
+import 'package:image_picker/image_picker.dart';
+
+// final ImagePicker _picker = ImagePicker();
+// // Pick an image
+// final XFile? image = await _picker.pickImage(source: ImageSource.gallery);
+// // Capture a photo
+// final XFile? photo = await _picker.pickImage(source: ImageSource.camera);
+// // Pick a video
+// final XFile? image = await _picker.pickVideo(source: ImageSource.gallery);
+// // Capture a video
+// final XFile? video = await _picker.pickVideo(source: ImageSource.camera);
+// // Pick multiple images
+// final List? images = await _picker.pickMultiImage();
+class ImagePicker {
+  static Future pickImage() async {
+    final origin_image_picker.ImagePicker _picker =
+        origin_image_picker.ImagePicker();
+// Pick an image
+    final XFile? image = await _picker.pickImage(source: ImageSource.gallery);
+    return image?.path;
+  }
+}
diff --git a/study-app-tutorials/lib/component/sp/shared_preference_helper.dart b/study-app-tutorials/lib/component/sp/shared_preference_helper.dart
new file mode 100644
index 000000000..a793c9cee
--- /dev/null
+++ b/study-app-tutorials/lib/component/sp/shared_preference_helper.dart
@@ -0,0 +1,17 @@
+import 'package:shared_preferences/shared_preferences.dart';
+
+class SharedPreferenceHelper {
+  static Future _getInstance() {
+    return SharedPreferences.getInstance();
+  }
+
+  static Future set(String key, String value) async {
+    SharedPreferences _prefs = await _getInstance();
+    return _prefs.setString(key, value);
+  }
+
+  static Future get(String key, String defaultValue) async {
+    SharedPreferences _prefs = await _getInstance();
+    return _prefs.getString(key)??defaultValue;
+  }
+}
diff --git a/study-app-tutorials/lib/component/toast/Toasts.dart b/study-app-tutorials/lib/component/toast/Toasts.dart
new file mode 100644
index 000000000..c2aba250a
--- /dev/null
+++ b/study-app-tutorials/lib/component/toast/Toasts.dart
@@ -0,0 +1,15 @@
+import 'package:flutter/material.dart';
+import 'package:fluttertoast/fluttertoast.dart';
+
+class Toasts {
+  static void show(String msg) {
+    Fluttertoast.showToast(
+        msg: msg,
+        toastLength: Toast.LENGTH_SHORT,
+        gravity: ToastGravity.CENTER,
+        timeInSecForIosWeb: 1,
+        backgroundColor: Colors.black,
+        textColor: Colors.white,
+        fontSize: 16.0);
+  }
+}
diff --git a/study-app-tutorials/lib/constant/app_constant.dart b/study-app-tutorials/lib/constant/app_constant.dart
new file mode 100644
index 000000000..a868bc754
--- /dev/null
+++ b/study-app-tutorials/lib/constant/app_constant.dart
@@ -0,0 +1,10 @@
+
+
+const double galleryHeaderHeight = 64;
+
+const int appVersion = 1;
+
+
+
+
+
diff --git a/study-app-tutorials/lib/constant/color_constant.dart b/study-app-tutorials/lib/constant/color_constant.dart
new file mode 100644
index 000000000..2a283b41c
--- /dev/null
+++ b/study-app-tutorials/lib/constant/color_constant.dart
@@ -0,0 +1,12 @@
+
+import 'package:flutter/material.dart';
+
+class ColorConstant {
+
+  static Color app_bar_only_back_color = Colors.black;
+
+}
+
+
+
+
diff --git a/study-app-tutorials/lib/constant/error_code_constant.dart b/study-app-tutorials/lib/constant/error_code_constant.dart
new file mode 100644
index 000000000..e010538d5
--- /dev/null
+++ b/study-app-tutorials/lib/constant/error_code_constant.dart
@@ -0,0 +1,12 @@
+
+
+class ErrorCodeConstant{
+
+  static int success = 200;
+  static int unknown = 999;
+
+  static int loginSecurityCodeRequired = 1001;
+
+}
+
+
diff --git a/study-app-tutorials/lib/constant/http_constant.dart b/study-app-tutorials/lib/constant/http_constant.dart
new file mode 100644
index 000000000..4f00bdee2
--- /dev/null
+++ b/study-app-tutorials/lib/constant/http_constant.dart
@@ -0,0 +1,25 @@
+
+
+class HttpConstant{
+
+  static int server_not_response = 205;
+  static String server_not_response_msg = 'server not response';
+
+  static String url_login = "/oauth/token";
+  static String url_register = "/oauth/token";
+  static String url_user_profile = "/api/authUser/profile/detail";
+  static String url_validate_code = "/validate/code/generate";
+
+  static String url_tv_query_page = "/ignore/api/tv/query/page";
+  static String url_tv_set_favorite = "/ignore/android/tv/favorite/set";
+  static String url_settings_version_check = "/ignore/android/app/version/check/{version}";
+  static String url_settings_app_download = "/ignore/android/app/version/check/{version}";
+  static String url_settings_app_latest = "/ignore/android/app/version/latest";
+  static String url_settings_app_versions = "/ignore/android/app/version/query";
+  static String url_settings_app_feedback_query = "/ignore/feedbackMessage/query/page";
+  static String url_settings_app_feedback_insert = "/ignore/feedbackMessage/insert";
+  static String url_settings_app_feedback_delete = "/ignore/feedbackMessage/delete";
+
+}
+
+
diff --git a/study-app-tutorials/lib/constant/locale_constant.dart b/study-app-tutorials/lib/constant/locale_constant.dart
new file mode 100644
index 000000000..b2c0b7648
--- /dev/null
+++ b/study-app-tutorials/lib/constant/locale_constant.dart
@@ -0,0 +1,13 @@
+
+class LocaleConstant{
+
+  static String settings_upgrade_dialog = "settings.upgrade.dialog.title";
+  static String settings_upgrade_cancel = "settings.upgrade.cancel";
+
+  static String all_delete_confirm = "all.delete.confirm";
+
+  static String home_back_confirm = "home.back.confirm";
+
+}
+
+
diff --git a/study-app-tutorials/lib/constant/route_constant.dart b/study-app-tutorials/lib/constant/route_constant.dart
new file mode 100644
index 000000000..429c892f4
--- /dev/null
+++ b/study-app-tutorials/lib/constant/route_constant.dart
@@ -0,0 +1,38 @@
+
+
+class RouteNameConstant{
+
+  static String route_name_home = "home";
+  static String route_name_home_statistics = "home/statistics";
+  static String route_name_settings = "settings";
+  static String route_name_setting_dev_tool = "settings/develop";
+  static String route_name_setting_feedbacks = "settings/feedbacks";
+  static String route_name_about = "about";
+  static String route_name_about_versions = "about/versions";
+  static String route_name_about_author = "about/author";
+  static String route_name_apks = "apks";
+
+  static String route_name_family_events = "family/events";
+
+  static String route_name_login = "login";
+  static String route_name_register = "register";
+  static String route_name_register_verify = "register/verify";
+  static String route_name_register_finish = "register/finish";
+
+  static String route_name_profile = "profile";
+  static String route_name_forget_password = "forget/password";
+  static String route_name_forget_password_verify = "forget/password/verify";
+  static String route_name_forget_password_finish = "forget/password/finish";
+
+
+  static String route_name_name_card = "name/card";
+  static String route_name_name_card_edit = "name/card/edit";
+
+  static String route_name_env = "env/setting";
+  static String route_name_env_edit = "env/edit";
+
+
+
+}
+
+
diff --git a/study-app-tutorials/lib/constant/sp_constant.dart b/study-app-tutorials/lib/constant/sp_constant.dart
new file mode 100644
index 000000000..33c4884a7
--- /dev/null
+++ b/study-app-tutorials/lib/constant/sp_constant.dart
@@ -0,0 +1,22 @@
+class SpConstant {
+
+  static String protocol_key = "_protocol_key";
+  static String protocol_value_default = "http";
+  static String host_key = '_host_key';
+  static String host_value_default = '101.132.119.250';
+  static String port_key = '_port_key';
+  static String port_value_default = '8088';
+
+  static String environment_index = '_environment_index';
+
+  static String download_dir_cache_key = 'download_dir_cache_key';
+  static String download_dir_value_default = '/storage/emulated/0/Download';
+
+  static String token_access_key = '/token/access/key';
+  static String token_refresh_key = '/token/refresh/key';
+
+
+  static String mock_switch_cache_key = 'mock_switch_cache_key';
+  static String mock_switch_value_default = 'false';
+
+}
diff --git a/study-app-tutorials/lib/locale/supported_locales.dart b/study-app-tutorials/lib/locale/supported_locales.dart
new file mode 100644
index 000000000..8b89dd1d9
--- /dev/null
+++ b/study-app-tutorials/lib/locale/supported_locales.dart
@@ -0,0 +1,13 @@
+import 'package:flutter/material.dart';
+
+class SupportedLocales {
+  static Locale zhLocale = const Locale('zh', 'CN'); // 中文简体
+  static Locale enLocale = const Locale('en', 'US'); // 美国英语
+
+  static Locale defaultLocale = zhLocale; // 中文简体
+
+  static List locales = [
+    zhLocale,
+    enLocale,
+  ];
+}
diff --git a/study-app-tutorials/lib/locale/translations.dart b/study-app-tutorials/lib/locale/translations.dart
new file mode 100644
index 000000000..20c31cea3
--- /dev/null
+++ b/study-app-tutorials/lib/locale/translations.dart
@@ -0,0 +1,66 @@
+import 'dart:async';
+import 'dart:convert';
+
+import 'package:flutter/material.dart';
+import 'package:flutter/services.dart' show rootBundle;
+import 'package:tutorials/component/log/logs.dart';
+import 'package:tutorials/locale/supported_locales.dart';
+
+class Translations {
+  Translations(Locale locale) {
+    this.locale = locale;
+  }
+
+  Locale? locale;
+  static Map _localizedValues = {};
+
+  static String textOf(BuildContext context, String key) {
+    var text = _of(context)?.text(key);
+    return text ?? "";
+  }
+
+  static Translations? _of(BuildContext context) {
+    return Localizations.of(context, Translations);
+  }
+
+  String text(String key) {
+    if(_localizedValues[key] == null){
+      Logs.info('** $key not found');
+    }
+    return _localizedValues[key] ?? 'Unknown';
+  }
+
+  static Future load(Locale locale) async {
+    Logs.info("load is called...");
+    Translations translations = Translations(locale);
+    String jsonContent = await rootBundle
+        .loadString("assets/locale/i18n_${locale.languageCode}.json");
+    _localizedValues = json.decode(jsonContent);
+    return translations;
+  }
+
+  get currentLanguage => locale?.languageCode;
+}
+
+class TranslationsDelegate extends LocalizationsDelegate {
+  const TranslationsDelegate();
+
+  @override
+  bool isSupported(Locale locale) {
+    List locales = SupportedLocales.locales;
+    for (Locale locale in locales) {
+      if (locale.languageCode == locale.languageCode) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  @override
+  Future load(Locale locale) => Translations.load(locale);
+
+  @override
+  bool shouldReload(TranslationsDelegate old) => false;
+
+  static TranslationsDelegate delegate = const TranslationsDelegate();
+}
diff --git a/study-app-tutorials/lib/main.dart b/study-app-tutorials/lib/main.dart
new file mode 100644
index 000000000..0d432c6e6
--- /dev/null
+++ b/study-app-tutorials/lib/main.dart
@@ -0,0 +1,150 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_easyloading/flutter_easyloading.dart';
+import 'package:flutter_localizations/flutter_localizations.dart';
+import 'package:tutorials/component/permission/permission_helper.dart';
+import 'package:tutorials/constant/route_constant.dart';
+import 'package:tutorials/locale/supported_locales.dart';
+import 'package:tutorials/locale/translations.dart';
+import 'package:tutorials/route/route.dart';
+import 'package:tutorials/startup/application.dart';
+import 'package:tutorials/component/log/logs.dart';
+
+void main() {
+  runApp(MyApp());
+}
+
+class MyApp extends StatelessWidget {
+  // This widget is the root of your application.
+  @override
+  Widget build(BuildContext context) {
+    return MaterialApp(
+      title: 'hello',
+      theme: ThemeData(
+        // This is the theme of your application.
+        //
+        // Try running your application with "flutter run". You'll see the
+        // application has a blue toolbar. Then, without quitting the app, try
+        // changing the primarySwatch below to Colors.green and then invoke
+        // "hot reload" (press "r" in the console where you ran "flutter run",
+        // or simply save your changes to "hot reload" in a Flutter IDE).
+        // Notice that the counter didn't reset back to zero; the application
+        // is not restarted.
+        primarySwatch: Colors.blue,
+        // This makes the visual density adapt to the platform that you run
+        // the app on. For desktop platforms, the controls will be smaller and
+        // closer together (more dense) than on mobile platforms.
+        visualDensity: VisualDensity.adaptivePlatformDensity,
+      ),
+      home: MyHomePage(title: '小陆'),
+      // routes: RouteConfiguration.routes,
+
+      // 路由
+      onGenerateRoute: RouteConfiguration.onGenerateRoute,
+
+      locale: SupportedLocales.defaultLocale, //手动指定locale
+
+      localizationsDelegates: [
+        GlobalMaterialLocalizations.delegate,
+        GlobalWidgetsLocalizations.delegate,
+        TranslationsDelegate.delegate,
+      ],
+      supportedLocales: SupportedLocales.locales,
+      localeListResolutionCallback: (locale, supportedLocales) {
+        return null; // 这里必须返回null,CIA
+      },
+      builder: EasyLoading.init(),
+    );
+
+  }
+}
+
+class MyHomePage extends StatefulWidget {
+  MyHomePage({Key? key,required this.title}) : super(key: key);
+
+  // This widget is the home page of your application. It is stateful, meaning
+  // that it has a State object (defined below) that contains fields that affect
+  // how it looks.
+
+  // This class is the configuration for the state. It holds the values (in this
+  // case the title) provided by the parent (in this case the App widget) and
+  // used by the build method of the State. Fields in a Widget subclass are
+  // always marked "final".
+
+  final String title;
+
+  @override
+  _MyHomePageState createState() => _MyHomePageState();
+}
+
+class _MyHomePageState extends State {
+  @override
+  void initState() {
+    // TODO: implement initState
+    super.initState();
+    checkPermission();
+  }
+
+  void checkPermission() async {
+    bool isGranted = await PermissionHelper.requestPermissions()??false;
+    Logs.info('isGranted0 = ' + isGranted.toString());
+
+    if (isGranted) {
+      Application.init(context);
+      Navigator.of(context).pop(1);
+      toDefaultPage();
+    } else {
+      Navigator.of(context).pop(1);
+    }
+  }
+
+  void toDefaultPage() {
+    Navigator.of(context).pushNamed(RouteNameConstant.route_name_login, arguments: "hi");
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    // This method is rerun every time setState is called, for instance as done
+    // by the toHome method above.
+    //
+    // The Flutter framework has been optimized to make rerunning build methods
+    // fast, so that you can just rebuild anything that needs updating rather
+    // than having to individually change instances of widgets.
+    return Scaffold(
+      appBar: AppBar(
+        // Here we take the value from the MyHomePage object that was created by
+        // the App.build method, and use it to set our appbar title.
+        title: Text(widget.title),
+      ),
+      body: Center(
+        // Center is a layout widget. It takes a single child and positions it
+        // in the middle of the parent.
+        child: Column(
+          // Column is also a layout widget. It takes a list of children and
+          // arranges them vertically. By default, it sizes itself to fit its
+          // children horizontally, and tries to be as tall as its parent.
+          //
+          // Invoke "debug painting" (press "p" in the console, choose the
+          // "Toggle Debug Paint" action from the Flutter Inspector in Android
+          // Studio, or the "Toggle Debug Paint" command in Visual Studio Code)
+          // to see the wireframe for each widget.
+          //
+          // Column has various properties to control how it sizes itself and
+          // how it positions its children. Here we use mainAxisAlignment to
+          // center the children vertically; the main axis here is the vertical
+          // axis because Columns are vertical (the cross axis would be
+          // horizontal).
+          mainAxisAlignment: MainAxisAlignment.center,
+          children: [
+            Text(Translations.textOf(context,"main.title"),
+            ),
+          ],
+        ),
+      ),
+      floatingActionButton: FloatingActionButton(
+        onPressed: toDefaultPage,
+        tooltip: 'Main',
+        child: const Icon(Icons.arrow_forward_rounded),
+      ), // This trailing comma makes auto-formatting nicer for build methods.
+    );
+  }
+}
diff --git a/study-app-tutorials/lib/model/about_author_tab.dart b/study-app-tutorials/lib/model/about_author_tab.dart
new file mode 100644
index 000000000..75deba5ec
--- /dev/null
+++ b/study-app-tutorials/lib/model/about_author_tab.dart
@@ -0,0 +1,8 @@
+
+
+class TabTitle {
+  String key = '';
+  String text = '';
+
+  TabTitle(this.key, this.text);
+}
diff --git a/study-app-tutorials/lib/model/app_feedback.dart b/study-app-tutorials/lib/model/app_feedback.dart
new file mode 100644
index 000000000..9a33bc0ef
--- /dev/null
+++ b/study-app-tutorials/lib/model/app_feedback.dart
@@ -0,0 +1,72 @@
+class AppFeedback {
+  int? id;
+  int? fromUserId;
+  String? fromUserName;
+  int? toUserId;
+  String? toUserName;
+  String? msgContent;
+  String? readStatus;
+  String? sendTime;
+  String? readTime;
+  String? msgType;
+  String? createTime;
+  String? updateTime;
+  int? createStaffId;
+  int? updateStaffId;
+  String? deleteState;
+
+  AppFeedback(
+      {this.id,
+      this.fromUserId,
+      this.fromUserName,
+      this.toUserId,
+      this.toUserName,
+      this.msgContent,
+      this.readStatus,
+      this.sendTime,
+      this.readTime,
+      this.msgType,
+      this.createTime,
+      this.updateTime,
+      this.createStaffId,
+      this.updateStaffId,
+      this.deleteState});
+
+  AppFeedback.fromJson(Map json) {
+    id = json['id'];
+    fromUserId = json['fromUserId'];
+    fromUserName = json['fromUserName'];
+    toUserId = json['toUserId'];
+    toUserName = json['toUserName'];
+    msgContent = json['msgContent'];
+    readStatus = json['readStatus'];
+    sendTime = json['sendTime'];
+    readTime = json['readTime'];
+    msgType = json['msgType'];
+    createTime = json['createTime'];
+    updateTime = json['updateTime'];
+    createStaffId = json['createStaffId'];
+    updateStaffId = json['updateStaffId'];
+    deleteState = json['deleteState'];
+  }
+
+  Map toJson() {
+    final Map data = new Map();
+    data['id'] = this.id;
+    data['fromUserId'] = this.fromUserId;
+    data['fromUserName'] = this.fromUserName;
+    data['toUserId'] = this.toUserId;
+    data['toUserName'] = this.toUserName;
+    data['msgContent'] = this.msgContent;
+    data['readStatus'] = this.readStatus;
+    data['sendTime'] = this.sendTime;
+    data['readTime'] = this.readTime;
+    data['msgType'] = this.msgType;
+    data['createTime'] = this.createTime;
+    data['updateTime'] = this.updateTime;
+    data['createStaffId'] = this.createStaffId;
+    data['updateStaffId'] = this.updateStaffId;
+    data['deleteState'] = this.deleteState;
+    return data;
+  }
+}
diff --git a/study-app-tutorials/lib/model/app_version.dart b/study-app-tutorials/lib/model/app_version.dart
new file mode 100644
index 000000000..ef0aad0df
--- /dev/null
+++ b/study-app-tutorials/lib/model/app_version.dart
@@ -0,0 +1,72 @@
+
+class AppVersion {
+  int? id;
+  int? versionCode;
+  String? description;
+  int? fileId;
+  String? fileUrl;
+  String? versionName;
+  int? fileSize;
+  String? updateType;
+  DateTime? createTime;
+  Null? updateTime;
+  Null? createStaffId;
+  Null? updateStaffId;
+  Null? deleteState;
+
+  AppVersion(
+      {this.id,
+        this.versionCode,
+        this.description,
+        this.fileId,
+        this.fileUrl,
+        this.versionName,
+        this.fileSize,
+        this.updateType,
+        this.createTime,
+        this.updateTime,
+        this.createStaffId,
+        this.updateStaffId,
+        this.deleteState});
+
+  static AppVersion getCurrentVersionInfo(){
+    AppVersion appVersion = new AppVersion();
+    appVersion.versionCode = 1;
+    appVersion.versionName = "v1.0.0";
+    return appVersion;
+  }
+
+  AppVersion.fromJson(Map json) {
+    id = json['id'];
+    versionCode = json['versionCode'];
+    description = json['description'];
+    fileId = json['fileId'];
+    fileUrl = json['fileUrl'];
+    versionName = json['versionName'];
+    fileSize = json['fileSize'];
+    updateType = json['updateType'];
+    createTime = json['createTime'];
+    updateTime = json['updateTime'];
+    createStaffId = json['createStaffId'];
+    updateStaffId = json['updateStaffId'];
+    deleteState = json['deleteState'];
+  }
+
+  Map toJson() {
+    final Map data = new Map();
+    data['id'] = this.id;
+    data['versionCode'] = this.versionCode;
+    data['description'] = this.description;
+    data['fileId'] = this.fileId;
+    data['fileUrl'] = this.fileUrl;
+    data['versionName'] = this.versionName;
+    data['fileSize'] = this.fileSize;
+    data['updateType'] = this.updateType;
+    data['createTime'] = this.createTime;
+    data['updateTime'] = this.updateTime;
+    data['createStaffId'] = this.createStaffId;
+    data['updateStaffId'] = this.updateStaffId;
+    data['deleteState'] = this.deleteState;
+    return data;
+  }
+}
diff --git a/study-app-tutorials/lib/model/pagination.dart b/study-app-tutorials/lib/model/pagination.dart
new file mode 100644
index 000000000..1b75409f8
--- /dev/null
+++ b/study-app-tutorials/lib/model/pagination.dart
@@ -0,0 +1,22 @@
+
+class Pagination {
+  int? targetPage;
+  int? pageSize;
+  int? total;
+
+  Pagination({this.targetPage, this.pageSize, this.total});
+
+  Pagination.fromJson(Map json) {
+    targetPage = json['targetPage'];
+    pageSize = json['pageSize'];
+    total = json['total'];
+  }
+
+  Map toJson() {
+    final Map data = new Map();
+    data['targetPage'] = this.targetPage;
+    data['pageSize'] = this.pageSize;
+    data['total'] = this.total;
+    return data;
+  }
+}
\ No newline at end of file
diff --git a/study-app-tutorials/lib/model/request_host.dart b/study-app-tutorials/lib/model/request_host.dart
new file mode 100644
index 000000000..48cab4642
--- /dev/null
+++ b/study-app-tutorials/lib/model/request_host.dart
@@ -0,0 +1,7 @@
+
+class RequestHost {
+  String protocol = "";
+  String host = "";
+  String port = "";
+
+}
\ No newline at end of file
diff --git a/study-app-tutorials/lib/model/route_path.dart b/study-app-tutorials/lib/model/route_path.dart
new file mode 100644
index 000000000..667e4a691
--- /dev/null
+++ b/study-app-tutorials/lib/model/route_path.dart
@@ -0,0 +1,22 @@
+import 'package:flutter/material.dart';
+
+typedef PathWidgetBuilder = Widget Function(BuildContext, String);
+
+class RoutePath {
+  const RoutePath(this.pattern, this.builder);
+
+  /// A RegEx string for route matching.
+  final String pattern;
+
+  /// The builder for the associated pattern route. The first argument is the
+  /// [BuildContext] and the second argument a RegEx match if that is included
+  /// in the pattern.
+  ///
+  /// ```dart
+  /// Path(
+  ///   'r'^/demo/([\w-]+)$',
+  ///   (context, matches) => Page(argument: match),
+  /// )
+  /// ```
+  final PathWidgetBuilder builder;
+}
diff --git a/study-app-tutorials/lib/prop/http_prop.dart b/study-app-tutorials/lib/prop/http_prop.dart
new file mode 100644
index 000000000..cb57dc381
--- /dev/null
+++ b/study-app-tutorials/lib/prop/http_prop.dart
@@ -0,0 +1,20 @@
+import 'package:tutorials/component/cache/http_request_caches.dart';
+
+class HttpProp {
+  static String getBaseUrl() {
+    return HttpRequestCaches.getProtocol() +
+        "://" +
+        HttpRequestCaches.getHost() +
+        ":" +
+        HttpRequestCaches.getPort() +
+        "/tutorials";
+  }
+
+  static int getTimeout() {
+    return 5000;
+  }
+
+  static String getContentType() {
+    return "application/json; charset=utf-8";
+  }
+}
diff --git a/study-app-tutorials/lib/request/feedback_request.dart b/study-app-tutorials/lib/request/feedback_request.dart
new file mode 100644
index 000000000..a0b37151e
--- /dev/null
+++ b/study-app-tutorials/lib/request/feedback_request.dart
@@ -0,0 +1,127 @@
+import 'dart:collection';
+
+import 'package:tutorials/component/log/logs.dart';
+import 'package:tutorials/request/model/feedback/message_feedback.dart';
+import 'package:tutorials/request/model/feedback/message_feedback_delete_request_param.dart';
+import 'package:tutorials/request/model/feedback/message_feedback_delete_request_result.dart';
+import 'package:tutorials/request/model/feedback/message_feedback_insert_request_param.dart';
+import 'package:tutorials/request/model/feedback/message_feedback_insert_request_result.dart';
+import 'package:tutorials/request/model/feedback/message_feedback_query_request_param.dart';
+import 'package:tutorials/request/model/feedback/message_feedback_query_request_result.dart';
+
+class MessageFeedbackRequests {
+  static List list = [];
+
+  static Future query(
+      MessageFeedbackQueryRequestParam requestParam) async {
+    Logs.info('request param : ${requestParam?.toString()}');
+
+    // String url = HttpConstant.url_settings_version_check
+    //     .replaceAll("{version}", AppUtils.getCurrentVersion().toString());
+    // HttpResult httpResult = await HttpRequests.get(url, null, null);
+    // String? temp = httpResult?.responseBody;
+    // String responseBody = temp == null ? "" : temp;
+    // Logs.info("responseBody = ${responseBody}");
+    Map param = HashMap();
+    // param.putIfAbsent("param", () => userName);
+    // return HttpRequests.post(HttpConstant.url_login, param, null);
+    return Future.delayed(const Duration(seconds: 1), () => mock());
+  }
+
+  static Future insert(
+      MessageFeedbackInsertRequestParam requestParam) async {
+    Logs.info('request param : ${requestParam?.toString()}');
+
+    // String url = HttpConstant.url_settings_version_check
+    //     .replaceAll("{version}", AppUtils.getCurrentVersion().toString());
+    // HttpResult httpResult = await HttpRequests.get(url, null, null);
+    // String? temp = httpResult?.responseBody;
+    // String responseBody = temp == null ? "" : temp;
+    // Logs.info("responseBody = ${responseBody}");
+    Map param = HashMap();
+    // param.putIfAbsent("param", () => userName);
+    // return HttpRequests.post(HttpConstant.url_login, param, null);
+    return Future.delayed(
+        const Duration(seconds: 1), () => mock2(requestParam.feedback));
+  }
+
+  static Future delete(
+      MessageFeedbackDeleteRequestParam requestParam) async {
+    Logs.info('request param : ${requestParam?.toString()}');
+
+    // String url = HttpConstant.url_settings_version_check
+    //     .replaceAll("{version}", AppUtils.getCurrentVersion().toString());
+    // HttpResult httpResult = await HttpRequests.get(url, null, null);
+    // String? temp = httpResult?.responseBody;
+    // String responseBody = temp == null ? "" : temp;
+    // Logs.info("responseBody = ${responseBody}");
+    Map param = HashMap();
+    // param.putIfAbsent("param", () => userName);
+    // return HttpRequests.post(HttpConstant.url_login, param, null);
+    return Future.delayed(
+        const Duration(seconds: 1), () => mock3(requestParam.feedbackId));
+  }
+
+  static MessageFeedbackDeleteRequestResult mock3(int? feedbackId) {
+    if (feedbackId != null) {
+      int index = list.indexWhere((element) => element.id == feedbackId);
+      Logs.info('delete item:  $list[index].toString()');
+      list.removeAt(index);
+    }
+
+    MessageFeedbackDeleteRequestResult result =
+        MessageFeedbackDeleteRequestResult();
+    result.common.code = 200;
+    result.common.message = "ok";
+    Logs.info('request result : ${result?.toString()}');
+    return result;
+  }
+
+  static MessageFeedbackInsertRequestResult mock2(MessageFeedback? feedback) {
+    if (feedback != null) {
+      feedback.id = DateTime.now().microsecond;
+      list.add(feedback);
+    }
+
+    MessageFeedbackInsertRequestResult result =
+        MessageFeedbackInsertRequestResult();
+    result.common.code = 200;
+    result.common.message = "ok";
+    Logs.info('request result : ${result?.toString()}');
+    return result;
+  }
+
+  static MessageFeedbackQueryRequestResult mock() {
+    if (list.isEmpty) {
+      init();
+    }
+
+    MessageFeedbackQueryRequestResult result =
+        MessageFeedbackQueryRequestResult();
+    result.common.code = 200;
+    result.common.message = "ok";
+    result.feedbacks = list;
+
+    Logs.info('request result : ${result?.toString()}');
+    return result;
+  }
+
+  static void init() {
+    MessageFeedback feedback = MessageFeedback();
+    feedback.id = DateTime.now().microsecond;
+    feedback.fromUserId = 1235;
+    feedback.fromUserName = '张三';
+    feedback.fromUserIconUrl =
+        'https://avatars.githubusercontent.com/u/18094768?v=4';
+    feedback.toUserId = 1234;
+    feedback.toUserName = '李四';
+    feedback.msgContent = '这是留言内容,吧啦吧啦吧啦吧';
+    feedback.readStatus = 'read';
+    feedback.sendTime = '2022-06-06 10:24';
+    feedback.readTime = '2022-06-06 10:24';
+    feedback.msgType = 'default';
+    feedback.createTime = '2022-06-06 10:24';
+
+    list.add(feedback);
+  }
+}
diff --git a/study-app-tutorials/lib/request/file_upload_request.dart b/study-app-tutorials/lib/request/file_upload_request.dart
new file mode 100644
index 000000000..927dbab0b
--- /dev/null
+++ b/study-app-tutorials/lib/request/file_upload_request.dart
@@ -0,0 +1,35 @@
+import 'dart:collection';
+
+import 'package:dio/src/multipart_file.dart';
+import 'package:tutorials/component/log/logs.dart';
+import 'package:tutorials/request/model/setting/app_version_check_request_param.dart';
+import 'package:tutorials/request/model/setting/app_version_check_request_result.dart';
+import 'package:tutorials/request/model/upload/file_upload_param.dart';
+import 'package:tutorials/request/model/upload/file_upload_result.dart';
+
+class FileUploadRequests {
+  static Future upload(FileUploadParam param, Function progress){
+    // Logs.info('request param : ${param?.toString()}');
+
+    // String url = HttpConstant.url_settings_version_check
+    //     .replaceAll("{version}", AppUtils.getCurrentVersion().toString());
+    // HttpResult httpResult = await HttpRequests.get(url, null, null);
+    // String? temp = httpResult?.responseBody;
+    // String responseBody = temp == null ? "" : temp;
+    // Logs.info("responseBody = ${responseBody}");
+    Map param = HashMap();
+    // param.putIfAbsent("param", () => userName);
+    // return HttpRequests.post(HttpConstant.url_login, param, null);
+    return Future.delayed(const Duration(seconds: 1), () => mock());
+  }
+
+  static FileUploadResult mock() {
+    FileUploadResult result = FileUploadResult();
+    result.common.code = 200;
+    // result.common.code = ErrorCodeConstant.loginSecurityCodeRequired;
+    result.common.message = "404啦啦啦";
+    Logs.info('request result : ${result?.toString()}');
+    return result;
+  }
+
+}
diff --git a/study-app-tutorials/lib/request/forget_password_requests.dart b/study-app-tutorials/lib/request/forget_password_requests.dart
new file mode 100644
index 000000000..c6fb33ef3
--- /dev/null
+++ b/study-app-tutorials/lib/request/forget_password_requests.dart
@@ -0,0 +1,24 @@
+import 'dart:collection';
+
+import 'package:tutorials/component/log/logs.dart';
+import 'package:tutorials/request/model/forget/forget_password_request_param.dart';
+import 'package:tutorials/request/model/forget/forget_password_request_result.dart';
+
+class ForgetPasswordRequests {
+  static Future reset(
+      ForgetPasswordRequestParam requestParam) async {
+    Logs.info('request param : ${requestParam?.toString()}');
+    Map param = HashMap();
+    // param.putIfAbsent("param", () => userName);
+    // return HttpRequests.post(HttpConstant.url_login, param, null);
+    return Future.delayed(const Duration(seconds: 1), () => mock());
+  }
+
+  static ForgetPasswordRequestResult mock() {
+    ForgetPasswordRequestResult result = ForgetPasswordRequestResult();
+    result.common.code = 200;
+    // result.common.message = "404啦啦啦";
+    Logs.info('request result : ${result?.toString()}');
+    return result;
+  }
+}
diff --git a/study-app-tutorials/lib/request/login_requests.dart b/study-app-tutorials/lib/request/login_requests.dart
new file mode 100644
index 000000000..eab2425d8
--- /dev/null
+++ b/study-app-tutorials/lib/request/login_requests.dart
@@ -0,0 +1,97 @@
+import 'dart:collection';
+import 'dart:convert';
+
+import 'package:tutorials/component/cache/setting_caches.dart';
+import 'package:tutorials/component/cache/token_caches.dart';
+import 'package:tutorials/component/http/http_requests.dart';
+import 'package:tutorials/component/http/http_result.dart';
+import 'package:tutorials/component/log/logs.dart';
+import 'package:tutorials/constant/http_constant.dart';
+import 'package:tutorials/constant/sp_constant.dart';
+import 'package:tutorials/request/model/login/login_request_param.dart';
+import 'package:tutorials/request/model/login/login_request_result.dart';
+import 'package:tutorials/request/origin/auth_origin_result.dart';
+import 'package:tutorials/request/origin/login_origin_result.dart';
+import 'package:tutorials/request/origin/login_origin_result_mapping.dart';
+import 'package:tutorials/utils/crypt_utils.dart';
+
+class LoginRequests {
+
+  static Future tryLogin(
+      String token) async {
+    Logs.info('request param : ${token}');
+
+    Map param = HashMap();
+    Map header = HashMap();
+    header.putIfAbsent(
+        "Authorization", () => "Bearer ${token}");
+    Logs.info('request header : ${header?.toString()}');
+
+    return Future.value(
+        HttpRequests.post(HttpConstant.url_user_profile, param, header)
+            .then((value) => LoginOriginResultMapping.mapping(value)));
+
+  }
+
+  static Future _auth(LoginRequestParam requestParam) async {
+    Logs.info('request param : ${requestParam?.toString()}');
+    Map param = HashMap();
+    param.putIfAbsent("grant_type", () => 'password');
+    param.putIfAbsent("username", () => requestParam?.userName ?? '');
+    param.putIfAbsent("password", () => requestParam?.password ?? '');
+
+    Map header = HashMap();
+    String userAndPass = "client:secret";
+    header.putIfAbsent(
+        "Authorization", () => "Basic " + CryptUtils.encode(userAndPass));
+
+    return Future.value(
+        HttpRequests.post(HttpConstant.url_login, param, header).then((value) {
+          return AuthOriginResult.fromJson(jsonDecode(value.responseBody));
+    }));
+
+  }
+
+  static Future login(
+      LoginRequestParam requestParam) async {
+    Logs.info('request param : ${requestParam?.toString()}');
+    if(await SettingCaches.getMockSwitch() == 'true'){
+      return Future.delayed(const Duration(seconds: 1), () => mock());
+    }
+    AuthOriginResult auth = await _auth(requestParam);
+    Logs.info('request auth : ${auth.toJson()}');
+    if(auth?.code != 200){
+      LoginRequestResult result = LoginRequestResult();
+      result.common.code = auth.code??0;
+      result.common.message = auth.msg??'';
+      return result;
+    }
+
+    TokenCaches.cacheAccessToken(auth?.data?.accessToken??'');
+    TokenCaches.cacheRefreshToken(auth?.data?.refreshToken??'');
+
+    Map param = HashMap();
+    Map header = HashMap();
+    header.putIfAbsent(
+        "Authorization", () => "Bearer ${auth?.data?.accessToken ?? ''}");
+    Logs.info('request header : ${header?.toString()}');
+
+    return Future.value(
+        HttpRequests.post(HttpConstant.url_user_profile, param, header)
+            .then((value) => LoginOriginResultMapping.mapping(value)));
+
+  }
+
+  static LoginRequestResult mock() {
+    LoginRequestResult result = LoginRequestResult();
+    result.common.code = 200;
+    // result.common.code = ErrorCodeConstant.loginSecurityCodeRequired;
+    result.common.message = "404啦啦啦";
+    result.id = 12345;
+    result.userName = '小陆[已登陆]';
+    result.mail = 'bage@qq.com';
+    result.iconUrl = 'https://avatars.githubusercontent.com/u/18094768?v=4';
+    Logs.info('mock request result : ${result?.toString()}');
+    return result;
+  }
+}
diff --git a/study-app-tutorials/lib/request/model/AppVersionResult.dart b/study-app-tutorials/lib/request/model/AppVersionResult.dart
new file mode 100644
index 000000000..18f5e0879
--- /dev/null
+++ b/study-app-tutorials/lib/request/model/AppVersionResult.dart
@@ -0,0 +1,32 @@
+import '../../model/app_version.dart';
+
+class AppVersionResult {
+  int? code;
+  int? originCode;
+  String? msg;
+  Null? originMsg;
+  AppVersion? data;
+
+  AppVersionResult(
+      {this.code, this.originCode, this.msg, this.originMsg, this.data});
+
+  AppVersionResult.fromJson(Map json) {
+    code = json['code'];
+    originCode = json['originCode'];
+    msg = json['msg'];
+    originMsg = json['originMsg'];
+    data = json['data'] != null ? new AppVersion.fromJson(json['data']) : null;
+  }
+
+  Map toJson() {
+    final Map data = new Map();
+    data['code'] = this.code;
+    data['originCode'] = this.originCode;
+    data['msg'] = this.msg;
+    data['originMsg'] = this.originMsg;
+    if (this.data != null) {
+      data['data'] = this.data?.toJson();
+    }
+    return data;
+  }
+}
diff --git a/study-app-tutorials/lib/request/model/AppVersionsResult.dart b/study-app-tutorials/lib/request/model/AppVersionsResult.dart
new file mode 100644
index 000000000..a50185488
--- /dev/null
+++ b/study-app-tutorials/lib/request/model/AppVersionsResult.dart
@@ -0,0 +1,71 @@
+import '../../model/app_version.dart';
+
+class AppVersionsResult {
+  int? code;
+  int? originCode;
+  String? msg;
+  String? originMsg;
+  List? data;
+  Pagination? pagination;
+
+  AppVersionsResult(
+      {this.code,
+      this.originCode,
+      this.msg,
+      this.originMsg,
+      this.data,
+      this.pagination});
+
+  AppVersionsResult.fromJson(Map json) {
+    code = json['code'];
+    originCode = json['originCode'];
+    msg = json['msg'];
+    originMsg = json['originMsg'];
+    if (json['data'] != null) {
+      data = [];
+      json['data'].forEach((v) {
+        data?.add(new AppVersion.fromJson(v));
+      });
+    }
+    pagination = json['pagination'] != null
+        ? new Pagination.fromJson(json['pagination'])
+        : null;
+  }
+
+  Map toJson() {
+    final Map data = new Map();
+    data['code'] = this.code;
+    data['originCode'] = this.originCode;
+    data['msg'] = this.msg;
+    data['originMsg'] = this.originMsg;
+    if (this.data != null) {
+      data['data'] = this.data?.map((v) => v.toJson()).toList();
+    }
+    if (this.pagination != null) {
+      data['pagination'] = this.pagination?.toJson();
+    }
+    return data;
+  }
+}
+
+class Pagination {
+  int? targetPage;
+  int? pageSize;
+  int? total;
+
+  Pagination({this.targetPage, this.pageSize, this.total});
+
+  Pagination.fromJson(Map json) {
+    targetPage = json['targetPage'];
+    pageSize = json['pageSize'];
+    total = json['total'];
+  }
+
+  Map toJson() {
+    final Map data = new Map();
+    data['targetPage'] = this.targetPage;
+    data['pageSize'] = this.pageSize;
+    data['total'] = this.total;
+    return data;
+  }
+}
diff --git a/study-app-tutorials/lib/request/model/AuthorInfo.dart b/study-app-tutorials/lib/request/model/AuthorInfo.dart
new file mode 100644
index 000000000..21fb80040
--- /dev/null
+++ b/study-app-tutorials/lib/request/model/AuthorInfo.dart
@@ -0,0 +1,11 @@
+
+class AuthorInfo {
+  int? id;
+  String? firstName;
+  String? lastName;
+  String? iconUrl;
+  String? homePageUrl;
+  String? description;
+  String? email;
+
+}
diff --git a/study-app-tutorials/lib/request/model/FeedbackQueryResult.dart b/study-app-tutorials/lib/request/model/FeedbackQueryResult.dart
new file mode 100644
index 000000000..acd0fc6ac
--- /dev/null
+++ b/study-app-tutorials/lib/request/model/FeedbackQueryResult.dart
@@ -0,0 +1,52 @@
+import '../../model/app_feedback.dart';
+import '../../model/pagination.dart';
+
+class FeedbackQueryResult {
+  int? code;
+  int? originCode;
+  String? msg;
+  String? originMsg;
+  List? data;
+  Pagination? pagination;
+
+  FeedbackQueryResult(
+      {this.code,
+        this.originCode,
+        this.msg,
+        this.originMsg,
+        this.data,
+        this.pagination});
+
+  FeedbackQueryResult.fromJson(Map json) {
+    code = json['code'];
+    originCode = json['originCode'];
+    msg = json['msg'];
+    originMsg = json['originMsg'];
+    if (json['data'] != null) {
+      data = [];
+      json['data'].forEach((v) {
+        data?.add(new AppFeedback.fromJson(v));
+      });
+    }
+    pagination = json['pagination'] != null
+        ? new Pagination.fromJson(json['pagination'])
+        : null;
+  }
+
+  Map toJson() {
+    final Map data = new Map();
+    data['code'] = this.code;
+    data['originCode'] = this.originCode;
+    data['msg'] = this.msg;
+    data['originMsg'] = this.originMsg;
+    if (this.data != null) {
+      data['data'] = this.data?.map((v) => v.toJson()).toList();
+    }
+    if (this.pagination != null) {
+      data['pagination'] = this.pagination?.toJson();
+    }
+    return data;
+  }
+}
+
+
diff --git a/study-app-tutorials/lib/request/model/common/common_param.dart b/study-app-tutorials/lib/request/model/common/common_param.dart
new file mode 100644
index 000000000..fbbed5033
--- /dev/null
+++ b/study-app-tutorials/lib/request/model/common/common_param.dart
@@ -0,0 +1,25 @@
+class CommonParam {
+  CommonParam();
+  String? traceId;
+  String? deviceId;
+  String? version;
+
+  CommonParam.fromJson(Map json){
+    traceId = json['traceId'];
+    deviceId = json['deviceId'];
+    version = json['version'];
+  }
+
+  Map toJson() {
+    final _data = {};
+    _data['traceId'] = traceId;
+    _data['deviceId'] = deviceId;
+    _data['version'] = version;
+    return _data;
+  }
+
+  @override
+  String toString() {
+    return 'CommonParam{traceId: $traceId, deviceId: $deviceId, version: $version}';
+  }
+}
\ No newline at end of file
diff --git a/study-app-tutorials/lib/request/model/common/common_result.dart b/study-app-tutorials/lib/request/model/common/common_result.dart
new file mode 100644
index 000000000..50fa43ca1
--- /dev/null
+++ b/study-app-tutorials/lib/request/model/common/common_result.dart
@@ -0,0 +1,11 @@
+
+class CommonResult {
+
+  int code = 0;
+  String message = '';
+
+  @override
+  String toString() {
+    return 'CommonResult{code: $code, message: $message}';
+  }
+}
\ No newline at end of file
diff --git a/study-app-tutorials/lib/request/model/common/page_query_param.dart b/study-app-tutorials/lib/request/model/common/page_query_param.dart
new file mode 100644
index 000000000..ea4bf7de6
--- /dev/null
+++ b/study-app-tutorials/lib/request/model/common/page_query_param.dart
@@ -0,0 +1,23 @@
+class PageQueryParam {
+  PageQueryParam();
+  int targetPage = 1;
+  int pageSize = 10;
+
+  
+  PageQueryParam.fromJson(Map json){
+    targetPage = json['targetPage'];
+    pageSize = json['pageSize'];
+  }
+
+  Map toJson() {
+    final _data = {};
+    _data['targetPage'] = targetPage;
+    _data['pageSize'] = pageSize;
+    return _data;
+  }
+
+  @override
+  String toString() {
+    return 'PageQueryParam{targetPage: $targetPage, pageSize: $pageSize';
+  }
+}
\ No newline at end of file
diff --git a/study-app-tutorials/lib/request/model/feedback/message_feedback.dart b/study-app-tutorials/lib/request/model/feedback/message_feedback.dart
new file mode 100644
index 000000000..cda3b83cc
--- /dev/null
+++ b/study-app-tutorials/lib/request/model/feedback/message_feedback.dart
@@ -0,0 +1,16 @@
+class MessageFeedback{
+  int? id;
+  int? fromUserId;
+  String? fromUserName;
+  String? fromUserIconUrl;
+  int? toUserId;
+  String? toUserName;
+  String? msgContent;
+  String? readStatus;
+  String? sendTime;
+  String? readTime;
+  String? msgType;
+  String? createTime;
+
+
+}
\ No newline at end of file
diff --git a/study-app-tutorials/lib/request/model/feedback/message_feedback_delete_request_param.dart b/study-app-tutorials/lib/request/model/feedback/message_feedback_delete_request_param.dart
new file mode 100644
index 000000000..29d3f1b06
--- /dev/null
+++ b/study-app-tutorials/lib/request/model/feedback/message_feedback_delete_request_param.dart
@@ -0,0 +1,14 @@
+import 'package:tutorials/request/model/common/common_param.dart';
+import 'package:tutorials/request/model/common/page_query_param.dart';
+import 'package:tutorials/request/model/feedback/message_feedback.dart';
+
+class MessageFeedbackDeleteRequestParam {
+  CommonParam common = CommonParam();
+
+  int? feedbackId;
+
+  @override
+  String toString() {
+    return 'MessageFeedbackQueryRequestParam{common: $common, feedbackId: $feedbackId}';
+  }
+}
diff --git a/study-app-tutorials/lib/request/model/feedback/message_feedback_delete_request_result.dart b/study-app-tutorials/lib/request/model/feedback/message_feedback_delete_request_result.dart
new file mode 100644
index 000000000..21349c311
--- /dev/null
+++ b/study-app-tutorials/lib/request/model/feedback/message_feedback_delete_request_result.dart
@@ -0,0 +1,6 @@
+import 'package:tutorials/request/model/common/common_result.dart';
+import 'package:tutorials/request/model/feedback/message_feedback.dart';
+
+class MessageFeedbackDeleteRequestResult {
+  CommonResult common = CommonResult();
+}
diff --git a/study-app-tutorials/lib/request/model/feedback/message_feedback_insert_request_param.dart b/study-app-tutorials/lib/request/model/feedback/message_feedback_insert_request_param.dart
new file mode 100644
index 000000000..6c1561248
--- /dev/null
+++ b/study-app-tutorials/lib/request/model/feedback/message_feedback_insert_request_param.dart
@@ -0,0 +1,14 @@
+import 'package:tutorials/request/model/common/common_param.dart';
+import 'package:tutorials/request/model/common/page_query_param.dart';
+import 'package:tutorials/request/model/feedback/message_feedback.dart';
+
+class MessageFeedbackInsertRequestParam {
+  CommonParam common = CommonParam();
+
+  MessageFeedback? feedback;
+
+  @override
+  String toString() {
+    return 'MessageFeedbackQueryRequestParam{common: $common, feedback: $feedback}';
+  }
+}
diff --git a/study-app-tutorials/lib/request/model/feedback/message_feedback_insert_request_result.dart b/study-app-tutorials/lib/request/model/feedback/message_feedback_insert_request_result.dart
new file mode 100644
index 000000000..887d537e7
--- /dev/null
+++ b/study-app-tutorials/lib/request/model/feedback/message_feedback_insert_request_result.dart
@@ -0,0 +1,6 @@
+import 'package:tutorials/request/model/common/common_result.dart';
+import 'package:tutorials/request/model/feedback/message_feedback.dart';
+
+class MessageFeedbackInsertRequestResult {
+  CommonResult common = CommonResult();
+}
diff --git a/study-app-tutorials/lib/request/model/feedback/message_feedback_query_request_param.dart b/study-app-tutorials/lib/request/model/feedback/message_feedback_query_request_param.dart
new file mode 100644
index 000000000..5e77a319b
--- /dev/null
+++ b/study-app-tutorials/lib/request/model/feedback/message_feedback_query_request_param.dart
@@ -0,0 +1,15 @@
+import 'package:tutorials/request/model/common/common_param.dart';
+import 'package:tutorials/request/model/common/page_query_param.dart';
+
+class MessageFeedbackQueryRequestParam {
+  CommonParam common = CommonParam();
+  PageQueryParam page = PageQueryParam();
+
+  String? keyword;
+  int? fromUserId;
+
+  @override
+  String toString() {
+    return 'FeedbackQueryRequestParam{common: $common, page: $page, keyword: $keyword, fromUserId: $fromUserId}';
+  }
+}
diff --git a/study-app-tutorials/lib/request/model/feedback/message_feedback_query_request_result.dart b/study-app-tutorials/lib/request/model/feedback/message_feedback_query_request_result.dart
new file mode 100644
index 000000000..c95b2f00b
--- /dev/null
+++ b/study-app-tutorials/lib/request/model/feedback/message_feedback_query_request_result.dart
@@ -0,0 +1,11 @@
+
+import 'package:tutorials/request/model/common/common_result.dart';
+import 'package:tutorials/request/model/feedback/message_feedback.dart';
+
+class MessageFeedbackQueryRequestResult {
+
+  CommonResult common = CommonResult();
+
+  List feedbacks = [];
+
+}
diff --git a/study-app-tutorials/lib/request/model/forget/forget_password_request_param.dart b/study-app-tutorials/lib/request/model/forget/forget_password_request_param.dart
new file mode 100644
index 000000000..c31b8a110
--- /dev/null
+++ b/study-app-tutorials/lib/request/model/forget/forget_password_request_param.dart
@@ -0,0 +1,28 @@
+import 'package:tutorials/request/model/common/common_param.dart';
+
+class ForgetPasswordRequestParam {
+  String? userName;
+  String? securityCode;
+  CommonParam common = CommonParam();
+
+  ForgetPasswordRequestParam();
+
+  ForgetPasswordRequestParam.fromJson(Map json) {
+    userName = json['userName'];
+    securityCode = json['securityCode'];
+    common = CommonParam.fromJson(json['common']);
+  }
+
+  Map toJson() {
+    final _data = {};
+    _data['userName'] = userName;
+    _data['securityCode'] = securityCode;
+    _data['common'] = common.toJson();
+    return _data;
+  }
+
+  @override
+  String toString() {
+    return 'ForgetPasswordRequestParam{userName: $userName, securityCode: $securityCode, common: $common}';
+  }
+}
diff --git a/study-app-tutorials/lib/request/model/forget/forget_password_request_result.dart b/study-app-tutorials/lib/request/model/forget/forget_password_request_result.dart
new file mode 100644
index 000000000..04a9de77b
--- /dev/null
+++ b/study-app-tutorials/lib/request/model/forget/forget_password_request_result.dart
@@ -0,0 +1,12 @@
+
+import 'package:tutorials/request/model/common/common_result.dart';
+
+class ForgetPasswordRequestResult {
+
+  CommonResult common = CommonResult();
+
+  @override
+  String toString() {
+    return 'ForgetPasswordRequestResult{common: $common}';
+  }
+}
diff --git a/study-app-tutorials/lib/request/model/login/login_request_param.dart b/study-app-tutorials/lib/request/model/login/login_request_param.dart
new file mode 100644
index 000000000..670e22025
--- /dev/null
+++ b/study-app-tutorials/lib/request/model/login/login_request_param.dart
@@ -0,0 +1,14 @@
+import 'package:tutorials/request/model/common/common_param.dart';
+
+class LoginRequestParam {
+  String? userName;
+  String? password;
+  String? securityCode;
+
+  CommonParam common = CommonParam();
+
+  @override
+  String toString() {
+    return 'LoginRequestParam{userName: $userName, password: $password, securityCode: $securityCode, common: $common}';
+  }
+}
diff --git a/study-app-tutorials/lib/request/model/login/login_request_result.dart b/study-app-tutorials/lib/request/model/login/login_request_result.dart
new file mode 100644
index 000000000..de966b69a
--- /dev/null
+++ b/study-app-tutorials/lib/request/model/login/login_request_result.dart
@@ -0,0 +1,16 @@
+import '../common/common_result.dart';
+
+class LoginRequestResult {
+
+  CommonResult common = CommonResult();
+
+  int? id;
+  String? userName;
+  String? iconUrl;
+  String? mail;
+
+  @override
+  String toString() {
+    return 'LoginRequestResult{common: $common, id: $id, userName: $userName, iconUrl: $iconUrl, mail: $mail}';
+  }
+}
diff --git a/study-app-tutorials/lib/request/model/register/register_request_param.dart b/study-app-tutorials/lib/request/model/register/register_request_param.dart
new file mode 100644
index 000000000..d261ef551
--- /dev/null
+++ b/study-app-tutorials/lib/request/model/register/register_request_param.dart
@@ -0,0 +1,31 @@
+import 'package:tutorials/request/model/common/common_param.dart';
+
+class RegisterRequestParam {
+  String? userName;
+  String? password;
+  String? securityCode;
+  CommonParam common = CommonParam();
+
+  RegisterRequestParam();
+
+  RegisterRequestParam.fromJson(Map json) {
+    userName = json['userName'];
+    password = json['password'];
+    securityCode = json['securityCode'];
+    common = CommonParam.fromJson(json['common']);
+  }
+
+  Map toJson() {
+    final _data = {};
+    _data['userName'] = userName;
+    _data['password'] = password;
+    _data['securityCode'] = securityCode;
+    _data['common'] = common.toJson();
+    return _data;
+  }
+
+  @override
+  String toString() {
+    return 'RegisterRequestParam{userName: $userName, password: $password, securityCode: $securityCode, common: $common}';
+  }
+}
diff --git a/study-app-tutorials/lib/request/model/register/register_request_result.dart b/study-app-tutorials/lib/request/model/register/register_request_result.dart
new file mode 100644
index 000000000..4738d6156
--- /dev/null
+++ b/study-app-tutorials/lib/request/model/register/register_request_result.dart
@@ -0,0 +1,12 @@
+import '../common/common_result.dart';
+
+class RegisterRequestResult {
+
+  CommonResult common = CommonResult();
+
+  @override
+  String toString() {
+    return 'RegisterRequestResult{common: $common}';
+  }
+
+}
diff --git a/study-app-tutorials/lib/request/model/security_code_request_param.dart b/study-app-tutorials/lib/request/model/security_code_request_param.dart
new file mode 100644
index 000000000..9f85b3d9f
--- /dev/null
+++ b/study-app-tutorials/lib/request/model/security_code_request_param.dart
@@ -0,0 +1,13 @@
+import 'package:tutorials/request/model/common/common_param.dart';
+
+class SecurityCodeRequestParam {
+
+  CommonParam common = CommonParam();
+
+  String clientId = "";
+
+  @override
+  String toString() {
+    return 'SecurityCodeRequestParam{common: $common, clientId: $clientId}';
+  }
+}
diff --git a/study-app-tutorials/lib/request/model/security_code_request_result.dart b/study-app-tutorials/lib/request/model/security_code_request_result.dart
new file mode 100644
index 000000000..39c365a99
--- /dev/null
+++ b/study-app-tutorials/lib/request/model/security_code_request_result.dart
@@ -0,0 +1,14 @@
+
+import 'package:tutorials/request/model/common/common_result.dart';
+
+class SecurityCodeRequestResult {
+
+  CommonResult common = CommonResult();
+
+  String url = '';
+
+  @override
+  String toString() {
+    return 'SecurityCodeRequestResult{common: $common, url: $url}';
+  }
+}
diff --git a/study-app-tutorials/lib/request/model/setting/app_version_check_request_param.dart b/study-app-tutorials/lib/request/model/setting/app_version_check_request_param.dart
new file mode 100644
index 000000000..017fbecd5
--- /dev/null
+++ b/study-app-tutorials/lib/request/model/setting/app_version_check_request_param.dart
@@ -0,0 +1,10 @@
+import 'package:tutorials/request/model/common/common_param.dart';
+
+class AppVersionCheckRequestParam {
+  CommonParam common = CommonParam();
+
+  @override
+  String toString() {
+    return 'LoginRequestParam{common: $common}';
+  }
+}
diff --git a/study-app-tutorials/lib/request/model/setting/app_version_check_request_result.dart b/study-app-tutorials/lib/request/model/setting/app_version_check_request_result.dart
new file mode 100644
index 000000000..0ff5d975a
--- /dev/null
+++ b/study-app-tutorials/lib/request/model/setting/app_version_check_request_result.dart
@@ -0,0 +1,17 @@
+
+import 'package:tutorials/request/model/common/common_result.dart';
+
+class AppVersionCheckRequestResult {
+
+  CommonResult common = CommonResult();
+
+  int? id;
+  int? versionCode;
+  String? description;
+  int? fileId;
+  String? fileUrl;
+  String? versionName;
+  int? fileSize;
+  String? updateType;
+
+}
diff --git a/study-app-tutorials/lib/request/model/upload/file_upload_param.dart b/study-app-tutorials/lib/request/model/upload/file_upload_param.dart
new file mode 100644
index 000000000..d4d1ac82b
--- /dev/null
+++ b/study-app-tutorials/lib/request/model/upload/file_upload_param.dart
@@ -0,0 +1,10 @@
+import 'package:dio/dio.dart';
+import 'package:tutorials/request/model/common/common_param.dart';
+
+class FileUploadParam {
+  CommonParam common = CommonParam();
+
+  Map? formData;
+  List? files;
+
+}
diff --git a/study-app-tutorials/lib/request/model/upload/file_upload_result.dart b/study-app-tutorials/lib/request/model/upload/file_upload_result.dart
new file mode 100644
index 000000000..fcdcb00a4
--- /dev/null
+++ b/study-app-tutorials/lib/request/model/upload/file_upload_result.dart
@@ -0,0 +1,8 @@
+
+import 'package:tutorials/request/model/common/common_result.dart';
+
+class FileUploadResult {
+
+  CommonResult common = CommonResult();
+
+}
diff --git a/study-app-tutorials/lib/request/model/user.dart b/study-app-tutorials/lib/request/model/user.dart
new file mode 100644
index 000000000..270aa462d
--- /dev/null
+++ b/study-app-tutorials/lib/request/model/user.dart
@@ -0,0 +1,19 @@
+
+import 'package:tutorials/request/model/login/login_request_result.dart';
+
+class User {
+  int? id;
+  String? userName;
+  String? iconUrl;
+  String? mail;
+
+  static User from(LoginRequestResult result) {
+    User user = User();
+    user.id = result.id;
+    user.userName = result.userName;
+    user.iconUrl = result.iconUrl;
+    user.mail = result.mail;
+    return user;
+  }
+
+}
diff --git a/study-app-tutorials/lib/request/model/user/detail/user_detail_param.dart b/study-app-tutorials/lib/request/model/user/detail/user_detail_param.dart
new file mode 100644
index 000000000..641c1afe0
--- /dev/null
+++ b/study-app-tutorials/lib/request/model/user/detail/user_detail_param.dart
@@ -0,0 +1,14 @@
+import 'package:tutorials/request/model/common/common_param.dart';
+
+class UserDetailParam {
+  String? userName;
+  String? password;
+  String? securityCode;
+
+  CommonParam common = CommonParam();
+
+  @override
+  String toString() {
+    return 'UserDetailParam{userName: $userName, password: $password, securityCode: $securityCode, common: $common}';
+  }
+}
diff --git a/study-app-tutorials/lib/request/model/user/detail/user_detail_result.dart b/study-app-tutorials/lib/request/model/user/detail/user_detail_result.dart
new file mode 100644
index 000000000..caa922436
--- /dev/null
+++ b/study-app-tutorials/lib/request/model/user/detail/user_detail_result.dart
@@ -0,0 +1,17 @@
+
+import 'package:tutorials/request/model/common/common_result.dart';
+
+class UserDetailResult {
+
+  CommonResult common = CommonResult();
+
+  int? id;
+  String? userName;
+  String? iconUrl;
+  String? mail;
+
+  @override
+  String toString() {
+    return 'UserDetailResult{common: $common, id: $id, userName: $userName, iconUrl: $iconUrl, mail: $mail}';
+  }
+}
diff --git a/study-app-tutorials/lib/request/origin/auth_origin_result.dart b/study-app-tutorials/lib/request/origin/auth_origin_result.dart
new file mode 100644
index 000000000..0fd14a0e4
--- /dev/null
+++ b/study-app-tutorials/lib/request/origin/auth_origin_result.dart
@@ -0,0 +1,71 @@
+class AuthOriginResult {
+  int? code;
+  int? originCode;
+  String? msg;
+  String? originMsg;
+  Data? data;
+
+  AuthOriginResult(
+      {this.code, this.originCode, this.msg, this.originMsg, this.data});
+
+  AuthOriginResult.fromJson(Map json) {
+    code = json['code'];
+    originCode = json['originCode'];
+    msg = json['msg'];
+    originMsg = json['originMsg'];
+    data = json['data'] != null ? new Data.fromJson(json['data']) : null;
+  }
+
+  Map toJson() {
+    final Map data = new Map();
+    data['code'] = this.code;
+    data['originCode'] = this.originCode;
+    data['msg'] = this.msg;
+    data['originMsg'] = this.originMsg;
+    if (this.data != null) {
+      data['data'] = this.data!.toJson();
+    }
+    return data;
+  }
+}
+
+class Data {
+  String? accessToken;
+  String? tokenType;
+  String? refreshToken;
+  int? expiresIn;
+  String? scope;
+  String? sub;
+  String? jti;
+
+  Data(
+      {this.accessToken,
+        this.tokenType,
+        this.refreshToken,
+        this.expiresIn,
+        this.scope,
+        this.sub,
+        this.jti});
+
+  Data.fromJson(Map json) {
+    accessToken = json['access_token'];
+    tokenType = json['token_type'];
+    refreshToken = json['refresh_token'];
+    expiresIn = json['expires_in'];
+    scope = json['scope'];
+    sub = json['sub'];
+    jti = json['jti'];
+  }
+
+  Map toJson() {
+    final Map data = new Map();
+    data['access_token'] = this.accessToken;
+    data['token_type'] = this.tokenType;
+    data['refresh_token'] = this.refreshToken;
+    data['expires_in'] = this.expiresIn;
+    data['scope'] = this.scope;
+    data['sub'] = this.sub;
+    data['jti'] = this.jti;
+    return data;
+  }
+}
diff --git a/study-app-tutorials/lib/request/origin/auth_origin_result_mapping.dart b/study-app-tutorials/lib/request/origin/auth_origin_result_mapping.dart
new file mode 100644
index 000000000..59d09b036
--- /dev/null
+++ b/study-app-tutorials/lib/request/origin/auth_origin_result_mapping.dart
@@ -0,0 +1,19 @@
+import 'dart:convert';
+
+import 'package:tutorials/component/http/http_result.dart';
+import 'package:tutorials/component/log/logs.dart';
+import 'package:tutorials/request/model/login/login_request_result.dart';
+import 'package:tutorials/request/origin/auth_origin_result.dart';
+import 'package:tutorials/request/origin/login_origin_result.dart';
+
+class AuthOriginResultMapping {
+
+  static AuthOriginResult mapping(HttpResult httpResult) {
+    Logs.info('httpResult ${httpResult.responseBody}');
+
+    AuthOriginResult originResult = AuthOriginResult.fromJson(jsonDecode(httpResult.responseBody));
+    return originResult;
+
+  }
+
+}
diff --git a/study-app-tutorials/lib/request/origin/login_origin_result.dart b/study-app-tutorials/lib/request/origin/login_origin_result.dart
new file mode 100644
index 000000000..8383297a0
--- /dev/null
+++ b/study-app-tutorials/lib/request/origin/login_origin_result.dart
@@ -0,0 +1,91 @@
+class LoginOriginResult {
+  int? code;
+  int? originCode;
+  String? msg;
+  String? originMsg;
+  Data? data;
+
+  LoginOriginResult(
+      {this.code, this.originCode, this.msg, this.originMsg, this.data});
+
+  LoginOriginResult.fromJson(Map json) {
+    code = json['code'];
+    originCode = json['originCode'];
+    msg = json['msg'];
+    originMsg = json['originMsg'];
+    data = json['data'] != null ? new Data.fromJson(json['data']) : null;
+  }
+
+  Map toJson() {
+    final Map data = new Map();
+    data['code'] = this.code;
+    data['originCode'] = this.originCode;
+    data['msg'] = this.msg;
+    data['originMsg'] = this.originMsg;
+    if (this.data != null) {
+      data['data'] = this.data!.toJson();
+    }
+    return data;
+  }
+}
+
+class Data {
+  int? id;
+  String? userName;
+  String? phone;
+  String? mail;
+  String? password;
+  String? sex;
+  Null? iconId;
+  Null? iconUrl;
+  String? birthday;
+  String? createTime;
+  String? updateTime;
+  Null? deleteState;
+
+  Data(
+      {this.id,
+        this.userName,
+        this.phone,
+        this.mail,
+        this.password,
+        this.sex,
+        this.iconId,
+        this.iconUrl,
+        this.birthday,
+        this.createTime,
+        this.updateTime,
+        this.deleteState});
+
+  Data.fromJson(Map json) {
+    id = json['id'];
+    userName = json['userName'];
+    phone = json['phone'];
+    mail = json['mail'];
+    password = json['password'];
+    sex = json['sex'];
+    iconId = json['iconId'];
+    iconUrl = json['iconUrl'];
+    birthday = json['birthday'];
+    createTime = json['createTime'];
+    updateTime = json['updateTime'];
+    deleteState = json['deleteState'];
+  }
+
+  Map toJson() {
+    final Map data = new Map();
+    data['id'] = this.id;
+    data['userName'] = this.userName;
+    data['phone'] = this.phone;
+    data['mail'] = this.mail;
+    data['password'] = this.password;
+    data['sex'] = this.sex;
+    data['iconId'] = this.iconId;
+    data['iconUrl'] = this.iconUrl;
+    data['birthday'] = this.birthday;
+    data['createTime'] = this.createTime;
+    data['updateTime'] = this.updateTime;
+    data['deleteState'] = this.deleteState;
+    return data;
+  }
+}
diff --git a/study-app-tutorials/lib/request/origin/login_origin_result_mapping.dart b/study-app-tutorials/lib/request/origin/login_origin_result_mapping.dart
new file mode 100644
index 000000000..82957cda0
--- /dev/null
+++ b/study-app-tutorials/lib/request/origin/login_origin_result_mapping.dart
@@ -0,0 +1,27 @@
+import 'dart:convert';
+
+import 'package:tutorials/component/http/http_result.dart';
+import 'package:tutorials/component/log/logs.dart';
+import 'package:tutorials/request/model/login/login_request_result.dart';
+import 'package:tutorials/request/origin/login_origin_result.dart';
+
+class LoginOriginResultMapping {
+
+  static LoginRequestResult mapping(HttpResult httpResult) {
+    Logs.info('httpResult ${httpResult.responseBody}');
+
+    LoginOriginResult originResult = LoginOriginResult.fromJson(jsonDecode(httpResult.responseBody));
+
+    LoginRequestResult result = LoginRequestResult();
+    result.common.code = originResult.code??0;
+    result.common.message = originResult.msg??'';
+
+    result.id = originResult.data?.id??0;
+    result.userName = originResult.data?.userName??'';
+    result.iconUrl = originResult.data?.iconUrl??'';
+    result.mail = originResult.data?.mail??'';
+    return result;
+
+  }
+
+}
diff --git a/study-app-tutorials/lib/request/register_requests.dart b/study-app-tutorials/lib/request/register_requests.dart
new file mode 100644
index 000000000..992cc865f
--- /dev/null
+++ b/study-app-tutorials/lib/request/register_requests.dart
@@ -0,0 +1,43 @@
+import 'dart:collection';
+
+import 'package:tutorials/component/http/http_requests.dart';
+import 'package:tutorials/component/log/logs.dart';
+import 'package:tutorials/constant/http_constant.dart';
+import 'package:tutorials/request/model/register/register_request_param.dart';
+import 'package:tutorials/request/model/register/register_request_result.dart';
+
+class RegisterRequests {
+  static Future register(
+      RegisterRequestParam requestParam) async {
+    Logs.info('request param : ${requestParam?.toString()}');
+    Map param = HashMap();
+    param.putIfAbsent("username", () => requestParam?.userName ?? '');
+    param.putIfAbsent("password", () => requestParam?.password ?? '');
+
+    Map header = HashMap();
+
+    // return Future.value(
+    //     HttpRequests.post(HttpConstant.url_login, param, header).then((value) {
+    //       return RegisterRequestResult.fromJson(jsonDecode(value.responseBody));
+    //     }));
+
+    return Future.delayed(const Duration(seconds: 1), () => mock());
+  }
+
+  static Future validSecurityCode(
+      RegisterRequestParam requestParam) async {
+    Logs.info('request param : ${requestParam?.toString()}');
+    Map param = HashMap();
+    // param.putIfAbsent("param", () => userName);
+    // return HttpRequests.post(HttpConstant.url_login, param, null);
+    return Future.delayed(const Duration(seconds: 1), () => mock());
+  }
+
+  static RegisterRequestResult mock() {
+    RegisterRequestResult result = RegisterRequestResult();
+    result.common.code = 200;
+    // result.common.message = "404啦啦啦";
+    Logs.info('request result : ${result?.toString()}');
+    return result;
+  }
+}
diff --git a/study-app-tutorials/lib/request/security_code_requests.dart b/study-app-tutorials/lib/request/security_code_requests.dart
new file mode 100644
index 000000000..849359b92
--- /dev/null
+++ b/study-app-tutorials/lib/request/security_code_requests.dart
@@ -0,0 +1,36 @@
+
+
+import 'dart:collection';
+import 'package:tutorials/component/log/logs.dart';
+import 'package:tutorials/constant/http_constant.dart';
+import 'package:tutorials/request/model/security_code_request_param.dart';
+import 'package:tutorials/request/model/security_code_request_result.dart';
+import 'package:tutorials/utils/app_utils.dart';
+
+
+class SecurityCodeRequests {
+
+  static String url(SecurityCodeRequestParam requestParam) {
+    Logs.info('request param : ${requestParam?.toString()}');
+    // return HttpConstant.url_validate_code + '?clientId=123456';
+    return 'https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fpic.616pic.com%2Fys_bnew_img%2F00%2F29%2F79%2F9UMK4fzdwr.jpg&refer=http%3A%2F%2Fpic.616pic.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1665674476&t=9eb76ad34a4addda1b09213e625ae99c';
+  }
+
+  static Future load(SecurityCodeRequestParam requestParam) async {
+    Logs.info('request param : ${requestParam?.toString()}');
+    Map param = HashMap();
+    // param.putIfAbsent("param", () => userName);
+    // return HttpRequests.post(HttpConstant.url_login, param, null);
+    return Future.delayed(const Duration(seconds: 1),() => mock());
+  }
+
+  static SecurityCodeRequestResult mock(){
+    SecurityCodeRequestResult result = SecurityCodeRequestResult();
+    result.common.code = 200;
+    // result.message = "404啦啦啦";
+    result.url = 'https://image.baidu.com/search/detail?ct=503316480&z=0&ipn=d&word=%E5%9B%BE%E7%89%87%20%E9%AA%8C%E8%AF%81%E7%A0%81&step_word=&hs=0&pn=3&spn=0&di=7077204560107798529&pi=0&rn=1&tn=baiduimagedetail&is=0%2C0&istype=0&ie=utf-8&oe=utf-8&in=&cl=2&lm=-1&st=undefined&cs=3617651942%2C1398176784&os=2089719489%2C3083133450&simid=3404834121%2C19490762&adpicid=0&lpn=0&ln=1767&fr=&fmq=1655655586039_R&fm=&ic=undefined&s=undefined&hd=undefined&latest=undefined©right=undefined&se=&sme=&tab=0&width=undefined&height=undefined&face=undefined&ist=&jit=&cg=&bdtype=0&oriquery=&objurl=https%3A%2F%2Fgimg2.baidu.com%2Fimage_search%2Fsrc%3Dhttp%3A%2F%2Fpic.616pic.com%2Fys_bnew_img%2F00%2F29%2F79%2F9UMK4fzdwr.jpg%26refer%3Dhttp%3A%2F%2Fpic.616pic.com%26app%3D2002%26size%3Df9999%2C10000%26q%3Da80%26n%3D0%26g%3D0n%26fmt%3Dauto%3Fsec%3D1658247603%26t%3Dcc4a8e5405c8fe5d28354a69412930b3&fromurl=ippr_z2C%24qAzdH3FAzdH3Fm8mrtv_z%26e3Bv54AzdH3Ff7vwtAzdH3Fzd6tshd2e_z%26e3Bip4s&gsm=4&rpstart=0&rpnum=0&islist=&querylist=&nojc=undefined&dyTabStr=MCwzLDUsMSw2LDQsNyw4LDIsOQ%3D%3D';
+    Logs.info('request result : ${result?.toString()}');
+    return result;
+  }
+
+}
diff --git a/study-app-tutorials/lib/request/setting_request.dart b/study-app-tutorials/lib/request/setting_request.dart
new file mode 100644
index 000000000..1a1f50c88
--- /dev/null
+++ b/study-app-tutorials/lib/request/setting_request.dart
@@ -0,0 +1,44 @@
+import 'dart:collection';
+
+import 'package:tutorials/component/log/logs.dart';
+import 'package:tutorials/request/model/setting/app_version_check_request_param.dart';
+import 'package:tutorials/request/model/setting/app_version_check_request_result.dart';
+
+class SettingRequests {
+  static Future checkVersion(
+      AppVersionCheckRequestParam requestParam) async {
+    Logs.info('request param : ${requestParam?.toString()}');
+
+    // String url = HttpConstant.url_settings_version_check
+    //     .replaceAll("{version}", AppUtils.getCurrentVersion().toString());
+    // HttpResult httpResult = await HttpRequests.get(url, null, null);
+    // String? temp = httpResult?.responseBody;
+    // String responseBody = temp == null ? "" : temp;
+    // Logs.info("responseBody = ${responseBody}");
+    Map param = HashMap();
+    // param.putIfAbsent("param", () => userName);
+    // return HttpRequests.post(HttpConstant.url_login, param, null);
+    return Future.delayed(const Duration(seconds: 1), () => mock());
+  }
+
+  static AppVersionCheckRequestResult mock() {
+    AppVersionCheckRequestResult result = AppVersionCheckRequestResult();
+    result.common.code = 200;
+    // result.common.code = ErrorCodeConstant.loginSecurityCodeRequired;
+    result.common.message = "404啦啦啦";
+    result.id = 12345;
+    result.versionCode = 2;
+    result.description = '版本信息\n'
+        '说明1\n'
+        '说明2\n'
+        '哈哈哈哈哈哈哈';
+    result.fileId = 12345;
+    result.fileUrl =
+        "https://raw.githubusercontent.com/bage2014/study/master/study-app-tutorials/apks/app-release.apk";
+    result.versionName = "Beta";
+    result.fileSize = 123;
+    result.updateType = "Default";
+    Logs.info('request result : ${result?.toString()}');
+    return result;
+  }
+}
diff --git a/study-app-tutorials/lib/request/user_detail_requests.dart b/study-app-tutorials/lib/request/user_detail_requests.dart
new file mode 100644
index 000000000..2cd4ec307
--- /dev/null
+++ b/study-app-tutorials/lib/request/user_detail_requests.dart
@@ -0,0 +1,44 @@
+import 'dart:collection';
+
+import 'package:tutorials/component/http/http_requests.dart';
+import 'package:tutorials/component/http/http_result.dart';
+import 'package:tutorials/component/log/logs.dart';
+import 'package:tutorials/constant/http_constant.dart';
+import 'package:tutorials/request/model/login/login_request_param.dart';
+import 'package:tutorials/request/model/login/login_request_result.dart';
+import 'package:tutorials/request/origin/login_origin_result_mapping.dart';
+import 'package:tutorials/utils/crypt_utils.dart';
+
+class LoginRequests {
+  static Future login(
+      LoginRequestParam requestParam) async {
+    Logs.info('request param : ${requestParam?.toString()}');
+    Map param = HashMap();
+    param.putIfAbsent("grant_type", () => 'password');
+    param.putIfAbsent("username", () => requestParam?.userName ?? '');
+    param.putIfAbsent("password", () => requestParam?.password ?? '');
+
+    Map header = HashMap();
+    String userAndPass = "client:secret";
+    header.putIfAbsent(
+        "Authorization", () => "Basic " + CryptUtils.encode(userAndPass));
+
+    return Future.value(HttpRequests.post(HttpConstant.url_login, param, header)
+        .then((value) => LoginOriginResultMapping.mapping(value)));
+
+    // return Future.delayed(const Duration(seconds: 1), () => mock());
+  }
+
+  static LoginRequestResult mock() {
+    LoginRequestResult result = LoginRequestResult();
+    result.common.code = 200;
+    // result.common.code = ErrorCodeConstant.loginSecurityCodeRequired;
+    result.common.message = "404啦啦啦";
+    result.id = 12345;
+    result.userName = '小陆[已登陆]';
+    result.mail = 'bage@qq.com';
+    result.iconUrl = 'https://avatars.githubusercontent.com/u/18094768?v=4';
+    Logs.info('request result : ${result?.toString()}');
+    return result;
+  }
+}
diff --git a/study-app-tutorials/lib/route/route.dart b/study-app-tutorials/lib/route/route.dart
new file mode 100644
index 000000000..a313270db
--- /dev/null
+++ b/study-app-tutorials/lib/route/route.dart
@@ -0,0 +1,147 @@
+import 'package:tutorials/component/log/logs.dart';
+import 'package:tutorials/view/home/statistics.dart';
+import 'package:tutorials/view/login/forget_password_finish.dart';
+import 'package:tutorials/view/login/login.dart';
+import 'package:tutorials/view/about/about_versions.dart';
+import 'package:tutorials/view/login/forget_password_verify.dart';
+import 'package:tutorials/view/login/register.dart';
+import 'package:tutorials/view/login/forget_password.dart';
+import 'package:tutorials/view/login/register_finish.dart';
+import 'package:tutorials/view/login/register_verify.dart';
+import 'package:tutorials/view/name.card/name_card.dart';
+import 'package:tutorials/view/name.card/name_card_edit.dart';
+import 'package:tutorials/view/settings/env.dart';
+import 'package:tutorials/view/settings/env_edit.dart';
+import 'package:tutorials/view/settings/feedbacks.dart';
+import 'package:flutter/material.dart';
+import 'package:tutorials/constant/route_constant.dart';
+import 'package:tutorials/model/route_path.dart';
+import 'package:tutorials/view/about/about.dart';
+import 'package:tutorials/view/home/home.dart';
+import 'package:tutorials/view/profile/profile.dart';
+import 'package:tutorials/view/about/about_author.dart';
+import 'package:tutorials/view/settings/dev_tool.dart';
+import 'package:tutorials/view/settings/settings.dart';
+
+class RouteConfiguration {
+  static List paths = [
+    RoutePath(
+      RouteNameConstant.route_name_home,
+      (context, match) => Home(),
+    ),
+    RoutePath(
+      RouteNameConstant.route_name_home_statistics,
+          (context, match) => Statistics(),
+    ),
+
+
+    RoutePath(
+      RouteNameConstant.route_name_login,
+          (context, match) => Login(),
+    ),
+    RoutePath(
+      RouteNameConstant.route_name_register,
+          (context, match) => Register(),
+    ),
+    RoutePath(
+      RouteNameConstant.route_name_register_verify,
+          (context, match) => RegisterVerify(),
+    ),
+    RoutePath(
+      RouteNameConstant.route_name_register_finish,
+          (context, match) => RegisterFinish(),
+    ),
+
+    RoutePath(
+      RouteNameConstant.route_name_forget_password,
+          (context, match) => ForgetPassword(),
+    ),
+    RoutePath(
+      RouteNameConstant.route_name_forget_password_verify,
+          (context, match) => ForgetPasswordVerify(),
+    ),
+    RoutePath(
+      RouteNameConstant.route_name_forget_password_finish,
+          (context, match) => ForgetPasswordFinish(),
+    ),
+
+    RoutePath(
+      RouteNameConstant.route_name_profile,
+          (context, match) => Profile(),
+    ),
+
+    RoutePath(
+      RouteNameConstant.route_name_settings,
+          (context, match) => Settings(),
+    ),
+    RoutePath(
+      RouteNameConstant.route_name_about,
+          (context, match) => About(),
+    ),
+    RoutePath(
+      RouteNameConstant.route_name_about_author,
+          (context, match) => AboutAuthor(),
+    ),
+    RoutePath(
+      RouteNameConstant.route_name_about_versions,
+          (context, match) => AboutVersions(),
+    ),
+    RoutePath(
+      RouteNameConstant.route_name_setting_dev_tool,
+          (context, match) => DevTool(),
+    ),
+    RoutePath(
+      RouteNameConstant.route_name_setting_feedbacks,
+          (context, match) => Feedbacks(),
+    ),
+
+    RoutePath(
+      RouteNameConstant.route_name_name_card,
+          (context, match) => NameCard(),
+    ),
+    RoutePath(
+      RouteNameConstant.route_name_name_card_edit,
+          (context, match) => NameCardEdit(),
+    ),
+    RoutePath(
+      RouteNameConstant.route_name_env,
+          (context, match) => Environment(),
+    ),
+    RoutePath(
+      RouteNameConstant.route_name_env_edit,
+          (context, match) => EnvironmentEdit(),
+    ),
+
+
+
+  ];
+
+  static Route? onGenerateRoute(RouteSettings settings) {
+    String? temp = settings?.name;
+    String name = temp ?? "";
+    Logs.info('routeName = ${name}');
+    // 有限匹配相等的
+    for (final path in paths) {
+      if (name.compareTo(path.pattern) == 0) {
+        return MaterialPageRoute(
+          builder: (context) => path.builder(context, path.pattern),
+          settings: settings,
+        );
+      }
+    }
+    // 在匹配正则的
+    for (final path in paths) {
+      final regExpPattern = RegExp(path.pattern);
+      if (regExpPattern.hasMatch(name)) {
+        final firstMatch = regExpPattern.firstMatch(name);
+        final match = (firstMatch?.groupCount == 1) ? firstMatch?.group(1) : null;
+        return MaterialPageRoute(
+          builder: (context) => path.builder(context, match == null ? "" : match),
+          settings: settings,
+        );
+      }
+    }
+    // If no match was found, we let [WidgetsApp.onUnknownRoute] handle it.
+    return null;
+  }
+}
diff --git a/study-app-tutorials/lib/startup/application.dart b/study-app-tutorials/lib/startup/application.dart
new file mode 100644
index 000000000..2c4d8bd73
--- /dev/null
+++ b/study-app-tutorials/lib/startup/application.dart
@@ -0,0 +1,15 @@
+import 'package:tutorials/component/cache/http_request_caches.dart';
+import 'package:tutorials/component/cache/user_caches.dart';
+import 'package:tutorials/component/http/http_requests.dart';
+import 'package:tutorials/utils/app_utils.dart';
+import 'package:flutter/cupertino.dart';
+
+class Application {
+  /// 应用初始化
+  static void init(BuildContext context) {
+    HttpRequestCaches.inits();
+    AppUtils.getDeviceId()
+        .then((deviceId) => {UserCaches.cacheUserId(deviceId.hashCode)});
+    HttpRequests.init();
+  }
+}
diff --git a/study-app-tutorials/lib/utils/app_utils.dart b/study-app-tutorials/lib/utils/app_utils.dart
new file mode 100644
index 000000000..17714d1fb
--- /dev/null
+++ b/study-app-tutorials/lib/utils/app_utils.dart
@@ -0,0 +1,72 @@
+import 'dart:io';
+
+import 'package:flutter/widgets.dart';
+import 'package:tutorials/component/log/logs.dart';
+import 'package:connectivity/connectivity.dart';
+import 'package:flutter/services.dart';
+import 'package:permission_handler/permission_handler.dart';
+import 'package:device_info/device_info.dart';
+
+class AppUtils {
+  static Future exitApp() async {
+    SystemChannels.platform.invokeMethod('SystemNavigator.pop');
+  }
+
+  static Future getDeviceId() async {
+    DeviceInfoPlugin deviceInfo = DeviceInfoPlugin();
+    if (Platform.isAndroid) {
+      AndroidDeviceInfo androidInfo = await deviceInfo.androidInfo;
+      print('android Running on ${androidInfo.model}'); // e.g. "Moto G (4)"
+      print('android id ${androidInfo.id}'); //
+      print('android id ${androidInfo.androidId}'); //
+      return androidInfo.id;
+    }
+    IosDeviceInfo iosInfo = await deviceInfo.iosInfo;
+    print('ios Running on ${iosInfo.utsname.machine}'); // e.g. "iPod7,1"
+    print('ios id ${iosInfo.identifierForVendor}'); // e.g. "iPod7,1"
+    print('ios name ${iosInfo.name}'); // e.g. "iPod7,1"
+    return iosInfo.identifierForVendor;
+  }
+
+  static Future isConnected() async {
+    var connectivityResult = await (Connectivity().checkConnectivity());
+    if (connectivityResult == ConnectivityResult.mobile) {
+      // I am connected to a mobile network.
+      Logs.info('I am connected to a mobile network.');
+      return true;
+    } else if (connectivityResult == ConnectivityResult.wifi) {
+      // I am connected to a wifi network.
+      Logs.info('I am connected to a wifi network.');
+      return true;
+    }
+    Logs.info('I am not connect to a network.');
+    return false;
+  }
+
+  static int getCurrentVersion() {
+    return 0;
+  }
+
+  static Future openSettings() {
+    return openAppSettings();
+  }
+
+  static String getPackageId() {
+    return "com.bage.flutter.tutorials";
+  }
+
+  static void toPage(
+    BuildContext context,
+    String route, {
+    Object? args,
+  }) {
+    Navigator.of(context).pushNamed(route, arguments: args);
+  }
+
+  static Object? getArgs(BuildContext context) {
+    return ModalRoute.of(context)?.settings?.arguments;
+  }
+  static void back(BuildContext context) {
+    Navigator.of(context).pop();
+  }
+}
diff --git a/study-app-tutorials/lib/utils/crypt_utils.dart b/study-app-tutorials/lib/utils/crypt_utils.dart
new file mode 100644
index 000000000..660557607
--- /dev/null
+++ b/study-app-tutorials/lib/utils/crypt_utils.dart
@@ -0,0 +1,14 @@
+import 'dart:convert';
+
+import 'dart:typed_data';
+
+class CryptUtils {
+  static String encode(String text) {
+    return base64Encode(utf8.encode(text));
+  }
+
+  static String decode(String text) {
+    Uint8List bytes = base64Decode(text);
+    return String.fromCharCodes(bytes);
+  }
+}
diff --git a/study-app-tutorials/lib/utils/date_time_utils.dart b/study-app-tutorials/lib/utils/date_time_utils.dart
new file mode 100644
index 000000000..9191f6b99
--- /dev/null
+++ b/study-app-tutorials/lib/utils/date_time_utils.dart
@@ -0,0 +1,32 @@
+class DateTimeUtils {
+  ///
+  /// 2021-07-26 12:31:05
+  ///
+  static DateTime parse(String? dateTimeStr) {
+    if (dateTimeStr == null) {
+      return DateTime.now();
+    }
+    return DateTime.parse(dateTimeStr);
+  }
+
+  ///
+  /// 2021-07-26T12:31:05
+  ///
+  static DateTime parseFromJson(String? dateTimeStr) {
+    if (dateTimeStr == null) {
+      return DateTime.now();
+    }
+    dateTimeStr = dateTimeStr.replaceAll('T', ' ');
+    return DateTime.parse(dateTimeStr);
+  }
+
+  ///
+  /// 2021-07-26T12:31:05
+  ///
+  static String format(DateTime dateTime) {
+    if (dateTime == null) {
+      dateTime = DateTime.now();
+    }
+    return dateTime.toIso8601String();
+  }
+}
diff --git a/study-app-tutorials/lib/utils/file_utils.dart b/study-app-tutorials/lib/utils/file_utils.dart
new file mode 100644
index 000000000..d67a15682
--- /dev/null
+++ b/study-app-tutorials/lib/utils/file_utils.dart
@@ -0,0 +1,33 @@
+import 'dart:io';
+
+import 'package:open_file/open_file.dart';
+import 'package:path_provider/path_provider.dart';
+import 'package:share/share.dart';
+import 'package:tutorials/component/cache/setting_caches.dart';
+import 'package:tutorials/component/log/logs.dart';
+import 'package:tutorials/component/sp/shared_preference_helper.dart';
+import 'package:tutorials/constant/sp_constant.dart';
+
+class FileUtils {
+  static void openFile(File file) {
+    OpenFile.open(file.path)
+        .then((value) => print('openFile then value = ${value.message}'))
+        .catchError((error) => {print('openFile catchError error = $error')});
+  }
+
+  static Future getDownloadDirectory() async {
+    return SettingCaches.getDownloadDirectory();
+  }
+
+  static Future write(File file, var bytes) async {
+    try {
+      var raf = file.openSync(mode: FileMode.write);
+      raf.writeFromSync(bytes);
+      await raf.close();
+    } catch (e) {
+      Logs.info(e.toString());
+      return false;
+    }
+    return true;
+  }
+}
diff --git a/study-app-tutorials/lib/view/about/about.dart b/study-app-tutorials/lib/view/about/about.dart
new file mode 100644
index 000000000..f19bb8e9f
--- /dev/null
+++ b/study-app-tutorials/lib/view/about/about.dart
@@ -0,0 +1,119 @@
+import 'package:tutorials/component/dialog/dialogs.dart';
+import 'package:tutorials/component/http/http_requests.dart';
+import 'package:tutorials/constant/http_constant.dart';
+import 'package:tutorials/model/app_version.dart';
+import 'package:tutorials/utils/app_utils.dart';
+import 'package:flutter/material.dart';
+import 'package:tutorials/constant/route_constant.dart';
+import 'package:tutorials/locale/translations.dart';
+import 'package:share/share.dart';
+
+class About extends StatefulWidget {
+  @override
+  _About createState() => new _About();
+}
+
+class _About extends State {
+
+  late AppVersion _currentVersionInfo;
+
+  @override
+  void initState() {
+    _currentVersionInfo = AppVersion.getCurrentVersionInfo();
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return Scaffold(
+      appBar: AppBar(
+        title: Text(Translations.textOf(context, "about.title")),
+      ),
+      body: Container(
+        alignment: Alignment.center,
+        child: Column(children: [
+          Container(
+            alignment: Alignment.center,
+            child: Row(
+              mainAxisAlignment: MainAxisAlignment.center,
+              children: [
+                Image(image: AssetImage("assets/images/logo128.png"))
+              ],
+            ),
+          ),
+          Container(
+            alignment: Alignment.center,
+            child: Row(
+              mainAxisAlignment: MainAxisAlignment.center,
+              children: [
+                Text(Translations.textOf(context, "all.app.name"),
+                    style:
+                        TextStyle(fontSize: 17.0, fontWeight: FontWeight.bold))
+              ],
+            ),
+          ),
+          Container(
+            alignment: Alignment.center,
+            child: Row(
+              mainAxisAlignment: MainAxisAlignment.center,
+              children: [
+                Text(_currentVersionInfo?.versionName??'')
+              ],
+            ),
+          ),
+          Container(
+            alignment: Alignment.center,
+            child: ListTile(
+              title: Text(Translations.textOf(context, "about.versions")),
+              trailing: Icon(Icons.chevron_right),
+              onTap: () {
+                Navigator.of(context)
+                    .pushNamed(RouteNameConstant.route_name_about_versions);
+              },
+            ),
+          ),
+          Container(
+            alignment: Alignment.center,
+            child: ListTile(
+              title: Text(Translations.textOf(context, "about.device.id")),
+              trailing: Icon(Icons.chevron_right),
+              onTap: () {
+                // 确认框
+                showDevice();
+              },
+            ),
+          ),
+          Container(
+            alignment: Alignment.center,
+            child: ListTile(
+              title: Text(Translations.textOf(context, "about.author")),
+              trailing: Icon(Icons.chevron_right),
+              onTap: () {
+                Navigator.of(context)
+                    .pushNamed(RouteNameConstant.route_name_about_author);
+              },
+            ),
+          ),
+          Container(
+            alignment: Alignment.center,
+            child: ListTile(
+              title: Text(Translations.textOf(context, "about.share")),
+              trailing: Icon(Icons.chevron_right),
+              onTap: () {
+                String url = HttpRequests.rebuildUrl(
+                    HttpConstant.url_settings_app_latest);
+                Share.share('【小陆,小陆,简简单单的小陆APP】 ${url} ', subject: '');
+              },
+            ),
+          ),
+        ]),
+      ),
+    );
+  }
+
+  void showDevice() {
+    AppUtils.getDeviceId().then((value) => {
+          Dialogs.showInfoDialog(
+              context, Translations.textOf(context, "about.device.id"), value)
+        });
+  }
+}
diff --git a/study-app-tutorials/lib/view/about/about_author.dart b/study-app-tutorials/lib/view/about/about_author.dart
new file mode 100644
index 000000000..b80146600
--- /dev/null
+++ b/study-app-tutorials/lib/view/about/about_author.dart
@@ -0,0 +1,121 @@
+import 'package:tutorials/locale/translations.dart';
+import 'package:tutorials/model/about_author_tab.dart';
+import 'package:tutorials/request/model/AuthorInfo.dart';
+import 'package:cached_network_image/cached_network_image.dart';
+import 'package:flutter/material.dart';
+
+import 'about_author_tab_view.dart';
+
+class AboutAuthor extends StatefulWidget {
+  @override
+  _AboutAuthor createState() => new _AboutAuthor();
+}
+
+class _AboutAuthor extends State
+    with SingleTickerProviderStateMixin {
+  late TabController _tabController; //需要定义一个Controller
+  List tabs = [];
+  late AuthorInfo authorInfo;
+  bool isLoading = true;
+
+  @override
+  void initState() {
+    tabs = AboutAuthorTabView.init();
+    _tabController = TabController(length: tabs.length, vsync: this);
+    authorInfo = new AuthorInfo();
+    authorInfo.iconUrl = "https://avatars.githubusercontent.com/u/18094768?v=4";
+    authorInfo.homePageUrl = "https://github.com/bage2014";
+    authorInfo.firstName = "陆";
+    authorInfo.lastName = "瑞华";
+    authorInfo.email = "893542907@qq.com";
+    authorInfo.description = "上海某互联网,Java 研发工程师,5年研发服务端经验";
+    isLoading = false;
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    const double edgeLeft = 16.0;
+    const double edgeRight = 16.0;
+    return Scaffold(
+      appBar: AppBar(
+        title: Text(Translations.textOf(context, "about.author.title")),
+      ),
+      body: Container(
+        alignment: Alignment.center,
+        child: Column(children: [
+          Container(
+            padding: const EdgeInsets.fromLTRB(edgeLeft, 16.0, edgeRight, 0.0),
+            child: ClipOval(
+              child: CachedNetworkImage(
+                imageUrl: authorInfo.iconUrl ?? '',
+                placeholder: (context, url) => CircularProgressIndicator(),
+                errorWidget: (context, url, error) =>
+                    Image(image: AssetImage("assets/images/user_null.png")),
+                height: 128,
+                width: 128,
+              ),
+            ),
+          ),
+          Container(
+            padding: const EdgeInsets.fromLTRB(edgeLeft, 0.0, edgeRight, 0.0),
+            child: Text('${authorInfo.firstName ?? ""}${authorInfo.lastName ?? ""}', textScaleFactor: 1.4),
+          ),
+          Container(
+            padding: const EdgeInsets.fromLTRB(edgeLeft, 0.0, edgeRight, 0.0),
+            child: TabBar(
+                //生成Tab菜单
+                controller: _tabController,
+                indicatorColor: Color(0xff66c97f),
+                //选中时下划线颜色,如果使用了indicator这里设置无效
+                labelColor: Color(0xff66c97f),
+                unselectedLabelColor: Colors.black,
+                tabs: tabs
+                    .map((e) => Tab(
+                          text: e.text,
+                        ))
+                    .toList()),
+          ),
+          Expanded(
+            child: Container(
+              padding: const EdgeInsets.fromLTRB(edgeLeft, 0.0, edgeRight, 0.0),
+              child: TabBarView(
+                controller: _tabController,
+                children: tabs.map((e) {
+                  //创建3个Tab页
+                  return isLoading ? LinearProgressIndicator(
+                    backgroundColor: Colors.grey[200],
+                    valueColor: AlwaysStoppedAnimation(Colors.blue),
+                  ) : Container(
+                    child: AboutAuthorTabView(tab: e,authorInfo: authorInfo),
+                  );
+                }).toList(),
+              ),
+            ),
+          ),
+        ]),
+      ),
+    );
+  }
+
+//    Map paramJson = new HashMap();
+//    paramJson.putIfAbsent("targetPage", () => 1);
+//    paramJson.putIfAbsent("pageSize", () => 100);
+//    Map param = new HashMap();
+//    param.putIfAbsent("param", () => json.encode(paramJson));
+//    print(json.encode(paramJson));
+//     list.add("hello");
+//    HttpRequests.get(HttpConstant.url_tv_query_page, param, null)
+//        .then((result) {
+//      Logs.info('_onRefresh responseBody=' + result?.responseBody);
+//      setState(() {
+//        QueryTvResult tvResult =
+//        QueryTvResult.fromJson(json.decode(result?.responseBody));
+//        if (tvResult.code == 200) {
+//          list = tvResult.data;
+//        }
+//      });
+//    }).catchError((error) {
+//      print(error.toString());
+//    });
+
+}
diff --git a/study-app-tutorials/lib/view/about/about_author_tab_view.dart b/study-app-tutorials/lib/view/about/about_author_tab_view.dart
new file mode 100644
index 000000000..60e65978c
--- /dev/null
+++ b/study-app-tutorials/lib/view/about/about_author_tab_view.dart
@@ -0,0 +1,244 @@
+import 'package:tutorials/locale/translations.dart';
+import 'package:tutorials/model/about_author_tab.dart';
+import 'package:tutorials/request/model/AuthorInfo.dart';
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+
+class AboutAuthorTabView extends StatelessWidget {
+  const AboutAuthorTabView({Key? key, required this.tab, required this.authorInfo}) : super(key: key);
+
+  final TabTitle tab;
+  final AuthorInfo authorInfo;
+  static const String key_basic = 'basic';
+  static const String key_activity = 'activity';
+
+  static List