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