├── .gitignore
├── .metadata
├── README.md
├── android
├── .gitignore
├── app
│ ├── build.gradle
│ └── src
│ │ └── main
│ │ ├── AndroidManifest.xml
│ │ ├── kotlin
│ │ └── im
│ │ │ └── juejin
│ │ │ └── juejinflutter
│ │ │ └── MainActivity.kt
│ │ └── res
│ │ ├── drawable
│ │ └── launch_background.xml
│ │ ├── mipmap-hdpi
│ │ └── ic_launcher.png
│ │ ├── mipmap-mdpi
│ │ └── ic_launcher.png
│ │ ├── mipmap-xhdpi
│ │ └── ic_launcher.png
│ │ ├── mipmap-xxhdpi
│ │ └── ic_launcher.png
│ │ ├── mipmap-xxxhdpi
│ │ └── ic_launcher.png
│ │ └── values
│ │ └── styles.xml
├── build.gradle
├── gradle.properties
├── gradle
│ └── wrapper
│ │ └── gradle-wrapper.properties
├── key.jks
├── proguard-rules.pro
└── settings.gradle
├── assets
├── loading.png
├── profile_arrow.png
├── splash_bg.webp
├── splash_logo.png
├── tab_activity.png
├── tab_activity_normal.png
├── tab_explore.png
├── tab_explore_normal.png
├── tab_home.png
├── tab_home_normal.png
├── tab_profile.png
├── tab_profile_normal.png
├── tab_xiaoce.png
├── tab_xiaoce_normal.png
├── timeline_comment.png
└── timeline_like_normal.png
├── ios
├── Flutter
│ ├── AppFrameworkInfo.plist
│ ├── Debug.xcconfig
│ └── Release.xcconfig
├── Runner.xcodeproj
│ ├── project.pbxproj
│ ├── project.xcworkspace
│ │ └── contents.xcworkspacedata
│ └── xcshareddata
│ │ └── xcschemes
│ │ └── Runner.xcscheme
├── Runner.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ └── WorkspaceSettings.xcsettings
└── Runner
│ ├── AppDelegate.swift
│ ├── Assets.xcassets
│ ├── AppIcon.appiconset
│ │ ├── Contents.json
│ │ ├── Icon-App-1024x1024@1x.png
│ │ ├── Icon-App-20x20@1x.png
│ │ ├── Icon-App-20x20@2x.png
│ │ ├── Icon-App-20x20@3x.png
│ │ ├── Icon-App-29x29@1x.png
│ │ ├── Icon-App-29x29@2x.png
│ │ ├── Icon-App-29x29@3x.png
│ │ ├── Icon-App-40x40@1x.png
│ │ ├── Icon-App-40x40@2x.png
│ │ ├── Icon-App-40x40@3x.png
│ │ ├── Icon-App-60x60@2x.png
│ │ ├── Icon-App-60x60@3x.png
│ │ ├── Icon-App-76x76@1x.png
│ │ ├── Icon-App-76x76@2x.png
│ │ └── Icon-App-83.5x83.5@2x.png
│ └── LaunchImage.imageset
│ │ ├── Contents.json
│ │ ├── LaunchImage.png
│ │ ├── LaunchImage@2x.png
│ │ ├── LaunchImage@3x.png
│ │ └── README.md
│ ├── Base.lproj
│ ├── LaunchScreen.storyboard
│ └── Main.storyboard
│ ├── Info.plist
│ └── Runner-Bridging-Header.h
├── lib
├── config
│ └── config_color.dart
├── main.dart
├── model
│ ├── category.dart
│ ├── entry.dart
│ ├── pin.dart
│ ├── tag.dart
│ ├── topic.dart
│ ├── user.dart
│ └── xiaoce.dart
├── page
│ ├── explorePage.dart
│ ├── homePage.dart
│ ├── logoPage.dart
│ ├── mainPage.dart
│ ├── picturePage.dart
│ ├── pinPage.dart
│ ├── userPage.dart
│ ├── webpage.dart
│ └── xiaocePage.dart
└── widget
│ └── banner
│ ├── banner_evalutor.dart
│ └── banner_widget.dart
├── pubspec.yaml
├── screenshots
├── 1.png
├── 2.png
├── 3.png
├── 4.png
├── 5.png
├── demo.apk
├── demo.gif
└── qr.png
└── test
└── widget_test.dart
/.gitignore:
--------------------------------------------------------------------------------
1 | # Miscellaneous
2 | *.class
3 | *.lock
4 | *.log
5 | *.pyc
6 | *.swp
7 | .DS_Store
8 | .atom/
9 | .buildlog/
10 | .history
11 | .svn/
12 |
13 | # IntelliJ related
14 | *.iml
15 | *.ipr
16 | *.iws
17 | .idea/
18 |
19 | # Visual Studio Code related
20 | .vscode/
21 |
22 | # Flutter/Dart/Pub related
23 | **/doc/api/
24 | .dart_tool/
25 | .flutter-plugins
26 | .packages
27 | .pub-cache/
28 | .pub/
29 | build/
30 |
31 | # Android related
32 | **/android/**/gradle-wrapper.jar
33 | **/android/.gradle
34 | **/android/captures/
35 | **/android/gradlew
36 | **/android/gradlew.bat
37 | **/android/local.properties
38 | **/android/**/GeneratedPluginRegistrant.java
39 |
40 | # iOS/XCode related
41 | **/ios/**/*.mode1v3
42 | **/ios/**/*.mode2v3
43 | **/ios/**/*.moved-aside
44 | **/ios/**/*.pbxuser
45 | **/ios/**/*.perspectivev3
46 | **/ios/**/*sync/
47 | **/ios/**/.sconsign.dblite
48 | **/ios/**/.tags*
49 | **/ios/**/.vagrant/
50 | **/ios/**/DerivedData/
51 | **/ios/**/Icon?
52 | **/ios/**/Pods/
53 | **/ios/**/.symlinks/
54 | **/ios/**/profile
55 | **/ios/**/xcuserdata
56 | **/ios/.generated/
57 | **/ios/Flutter/App.framework
58 | **/ios/Flutter/Flutter.framework
59 | **/ios/Flutter/Generated.xcconfig
60 | **/ios/Flutter/app.flx
61 | **/ios/Flutter/app.zip
62 | **/ios/Flutter/flutter_assets/
63 | **/ios/ServiceDefinitions.json
64 | **/ios/Runner/GeneratedPluginRegistrant.*
65 |
66 | # Exceptions to above rules.
67 | !**/ios/**/default.mode1v3
68 | !**/ios/**/default.mode2v3
69 | !**/ios/**/default.pbxuser
70 | !**/ios/**/default.perspectivev3
71 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
72 |
--------------------------------------------------------------------------------
/.metadata:
--------------------------------------------------------------------------------
1 | # This file tracks properties of this Flutter project.
2 | # Used by Flutter tool to assess capabilities and perform upgrades etc.
3 | #
4 | # This file should be version controlled and should not be manually edited.
5 |
6 | version:
7 | revision: f37c235c32fc15babe6dc7b7bc2ee4387e5ecf92
8 | channel: beta
9 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # juejin_flutter
2 |
3 | juejin client power by flutter
4 | 使用 Flutter 仿写的掘金客户端,自定义了9宫格图片布局
5 |
6 | [APK Download](https://raw.githubusercontent.com/hanks-zyh/juejin_flutter/master/screenshots/demo.apk)
7 |
8 |
9 |
10 | ## 截图
11 |
12 |
13 |

14 |

15 |

16 |

17 |

18 |

19 |
20 |
21 | ## 总结
22 |
23 | `Flutter` 相比于 `RN` `Weex` 写 UI 优势比较大,不存在平台不兼容或者丢失某些特性,但是同样的具有跨平台应用平台的通病,涉及到各自平台相关的特性就不得不再去适配各个平台,像是 数据库,地图,webView 等,目前好像是没有较好的解决方法。
24 |
--------------------------------------------------------------------------------
/android/.gitignore:
--------------------------------------------------------------------------------
1 | app/release/
2 |
--------------------------------------------------------------------------------
/android/app/build.gradle:
--------------------------------------------------------------------------------
1 | def localProperties = new Properties()
2 | def localPropertiesFile = rootProject.file('local.properties')
3 | if (localPropertiesFile.exists()) {
4 | localPropertiesFile.withReader('UTF-8') { reader ->
5 | localProperties.load(reader)
6 | }
7 | }
8 |
9 | def flutterRoot = localProperties.getProperty('flutter.sdk')
10 | if (flutterRoot == null) {
11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
12 | }
13 |
14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
15 | if (flutterVersionCode == null) {
16 | flutterVersionCode = '1'
17 | }
18 |
19 | def flutterVersionName = localProperties.getProperty('flutter.versionName')
20 | if (flutterVersionName == null) {
21 | flutterVersionName = '1.0'
22 | }
23 |
24 | apply plugin: 'com.android.application'
25 | apply plugin: 'kotlin-android'
26 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
27 |
28 |
29 | android {
30 | compileSdkVersion 27
31 |
32 | sourceSets {
33 | main.java.srcDirs += 'src/main/kotlin'
34 | }
35 |
36 | lintOptions {
37 | disable 'InvalidPackage'
38 | }
39 |
40 | defaultConfig {
41 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
42 | applicationId "im.juejin.juejinflutter"
43 | minSdkVersion 16
44 | targetSdkVersion 27
45 | versionCode flutterVersionCode.toInteger()
46 | versionName flutterVersionName
47 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
48 | }
49 |
50 | signingConfigs {
51 | release {
52 | keyAlias 'key'
53 | keyPassword '22e2030'
54 | storeFile file('../key.jks')
55 | storePassword '22e2030'
56 | }
57 | }
58 |
59 | buildTypes {
60 | release {
61 | signingConfig signingConfigs.release
62 | // minifyEnabled true
63 | // useProguard true
64 | // proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
65 | }
66 |
67 | }
68 | }
69 |
70 | flutter {
71 | source '../..'
72 | }
73 |
74 | dependencies {
75 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
76 | testImplementation 'junit:junit:4.12'
77 | androidTestImplementation 'com.android.support.test:runner:1.0.2'
78 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
79 | }
80 |
--------------------------------------------------------------------------------
/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
8 |
9 |
10 |
15 |
19 |
26 |
30 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/android/app/src/main/kotlin/im/juejin/juejinflutter/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package im.juejin.juejinflutter
2 |
3 | import android.os.Bundle
4 |
5 | import io.flutter.app.FlutterActivity
6 | import io.flutter.plugins.GeneratedPluginRegistrant
7 |
8 | class MainActivity(): FlutterActivity() {
9 | override fun onCreate(savedInstanceState: Bundle?) {
10 | super.onCreate(savedInstanceState)
11 | GeneratedPluginRegistrant.registerWith(this)
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hanks-zyh/juejin_flutter/5dcef5b1ec5e084f3dc60350e5fd9d7309f4742b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hanks-zyh/juejin_flutter/5dcef5b1ec5e084f3dc60350e5fd9d7309f4742b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hanks-zyh/juejin_flutter/5dcef5b1ec5e084f3dc60350e5fd9d7309f4742b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hanks-zyh/juejin_flutter/5dcef5b1ec5e084f3dc60350e5fd9d7309f4742b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hanks-zyh/juejin_flutter/5dcef5b1ec5e084f3dc60350e5fd9d7309f4742b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 |
--------------------------------------------------------------------------------
/android/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | ext.kotlin_version = '1.2.71'
3 | repositories {
4 | // google()
5 | // jcenter()
6 | maven { url 'https://maven.aliyun.com/repository/google' }
7 | maven { url 'https://maven.aliyun.com/repository/jcenter' }
8 | maven { url 'http://maven.aliyun.com/nexus/content/groups/public' }
9 | }
10 |
11 | dependencies {
12 | classpath 'com.android.tools.build:gradle:3.2.1'
13 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
14 | }
15 | }
16 |
17 | allprojects {
18 | repositories {
19 | // google()
20 | // jcenter()
21 | maven { url 'https://maven.aliyun.com/repository/google' }
22 | maven { url 'https://maven.aliyun.com/repository/jcenter' }
23 | maven { url 'http://maven.aliyun.com/nexus/content/groups/public' }
24 | }
25 | }
26 |
27 | rootProject.buildDir = '../build'
28 | subprojects {
29 | project.buildDir = "${rootProject.buildDir}/${project.name}"
30 | }
31 | subprojects {
32 | project.evaluationDependsOn(':app')
33 | }
34 |
35 | task clean(type: Delete) {
36 | delete rootProject.buildDir
37 | }
38 |
--------------------------------------------------------------------------------
/android/gradle.properties:
--------------------------------------------------------------------------------
1 | org.gradle.jvmargs=-Xmx1536M
2 |
--------------------------------------------------------------------------------
/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Fri Jun 23 08:50:38 CEST 2017
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip
7 |
--------------------------------------------------------------------------------
/android/key.jks:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hanks-zyh/juejin_flutter/5dcef5b1ec5e084f3dc60350e5fd9d7309f4742b/android/key.jks
--------------------------------------------------------------------------------
/android/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | #Flutter Wrapper
2 | -keep class io.flutter.app.** { *; }
3 | -keep class io.flutter.plugin.** { *; }
4 | -keep class io.flutter.util.** { *; }
5 | -keep class io.flutter.view.** { *; }
6 | -keep class io.flutter.** { *; }
7 | -keep class io.flutter.plugins.** { *; }
--------------------------------------------------------------------------------
/android/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
3 | def flutterProjectRoot = rootProject.projectDir.parentFile.toPath()
4 |
5 | def plugins = new Properties()
6 | def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins')
7 | if (pluginsFile.exists()) {
8 | pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) }
9 | }
10 |
11 | plugins.each { name, path ->
12 | def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile()
13 | include ":$name"
14 | project(":$name").projectDir = pluginDirectory
15 | }
16 |
--------------------------------------------------------------------------------
/assets/loading.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hanks-zyh/juejin_flutter/5dcef5b1ec5e084f3dc60350e5fd9d7309f4742b/assets/loading.png
--------------------------------------------------------------------------------
/assets/profile_arrow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hanks-zyh/juejin_flutter/5dcef5b1ec5e084f3dc60350e5fd9d7309f4742b/assets/profile_arrow.png
--------------------------------------------------------------------------------
/assets/splash_bg.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hanks-zyh/juejin_flutter/5dcef5b1ec5e084f3dc60350e5fd9d7309f4742b/assets/splash_bg.webp
--------------------------------------------------------------------------------
/assets/splash_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hanks-zyh/juejin_flutter/5dcef5b1ec5e084f3dc60350e5fd9d7309f4742b/assets/splash_logo.png
--------------------------------------------------------------------------------
/assets/tab_activity.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hanks-zyh/juejin_flutter/5dcef5b1ec5e084f3dc60350e5fd9d7309f4742b/assets/tab_activity.png
--------------------------------------------------------------------------------
/assets/tab_activity_normal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hanks-zyh/juejin_flutter/5dcef5b1ec5e084f3dc60350e5fd9d7309f4742b/assets/tab_activity_normal.png
--------------------------------------------------------------------------------
/assets/tab_explore.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hanks-zyh/juejin_flutter/5dcef5b1ec5e084f3dc60350e5fd9d7309f4742b/assets/tab_explore.png
--------------------------------------------------------------------------------
/assets/tab_explore_normal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hanks-zyh/juejin_flutter/5dcef5b1ec5e084f3dc60350e5fd9d7309f4742b/assets/tab_explore_normal.png
--------------------------------------------------------------------------------
/assets/tab_home.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hanks-zyh/juejin_flutter/5dcef5b1ec5e084f3dc60350e5fd9d7309f4742b/assets/tab_home.png
--------------------------------------------------------------------------------
/assets/tab_home_normal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hanks-zyh/juejin_flutter/5dcef5b1ec5e084f3dc60350e5fd9d7309f4742b/assets/tab_home_normal.png
--------------------------------------------------------------------------------
/assets/tab_profile.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hanks-zyh/juejin_flutter/5dcef5b1ec5e084f3dc60350e5fd9d7309f4742b/assets/tab_profile.png
--------------------------------------------------------------------------------
/assets/tab_profile_normal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hanks-zyh/juejin_flutter/5dcef5b1ec5e084f3dc60350e5fd9d7309f4742b/assets/tab_profile_normal.png
--------------------------------------------------------------------------------
/assets/tab_xiaoce.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hanks-zyh/juejin_flutter/5dcef5b1ec5e084f3dc60350e5fd9d7309f4742b/assets/tab_xiaoce.png
--------------------------------------------------------------------------------
/assets/tab_xiaoce_normal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hanks-zyh/juejin_flutter/5dcef5b1ec5e084f3dc60350e5fd9d7309f4742b/assets/tab_xiaoce_normal.png
--------------------------------------------------------------------------------
/assets/timeline_comment.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hanks-zyh/juejin_flutter/5dcef5b1ec5e084f3dc60350e5fd9d7309f4742b/assets/timeline_comment.png
--------------------------------------------------------------------------------
/assets/timeline_like_normal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hanks-zyh/juejin_flutter/5dcef5b1ec5e084f3dc60350e5fd9d7309f4742b/assets/timeline_like_normal.png
--------------------------------------------------------------------------------
/ios/Flutter/AppFrameworkInfo.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | App
9 | CFBundleIdentifier
10 | io.flutter.flutter.app
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | App
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1.0
23 | MinimumOSVersion
24 | 8.0
25 |
26 |
27 |
--------------------------------------------------------------------------------
/ios/Flutter/Debug.xcconfig:
--------------------------------------------------------------------------------
1 | #include "Generated.xcconfig"
2 |
--------------------------------------------------------------------------------
/ios/Flutter/Release.xcconfig:
--------------------------------------------------------------------------------
1 | #include "Generated.xcconfig"
2 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
11 | 2D5378261FAA1A9400D5DBA9 /* flutter_assets in Resources */ = {isa = PBXBuildFile; fileRef = 2D5378251FAA1A9400D5DBA9 /* flutter_assets */; };
12 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
13 | 3B80C3941E831B6300D905FE /* App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; };
14 | 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
15 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
16 | 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; };
17 | 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
18 | 9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 9740EEB21CF90195004384FC /* Debug.xcconfig */; };
19 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
20 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
21 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
22 | /* End PBXBuildFile section */
23 |
24 | /* Begin PBXCopyFilesBuildPhase section */
25 | 9705A1C41CF9048500538489 /* Embed Frameworks */ = {
26 | isa = PBXCopyFilesBuildPhase;
27 | buildActionMask = 2147483647;
28 | dstPath = "";
29 | dstSubfolderSpec = 10;
30 | files = (
31 | 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */,
32 | 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */,
33 | );
34 | name = "Embed Frameworks";
35 | runOnlyForDeploymentPostprocessing = 0;
36 | };
37 | /* End PBXCopyFilesBuildPhase section */
38 |
39 | /* Begin PBXFileReference section */
40 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; };
41 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; };
42 | 2D5378251FAA1A9400D5DBA9 /* flutter_assets */ = {isa = PBXFileReference; lastKnownFileType = folder; name = flutter_assets; path = Flutter/flutter_assets; sourceTree = SOURCE_ROOT; };
43 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; };
44 | 3B80C3931E831B6300D905FE /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/App.framework; sourceTree = ""; };
45 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; };
46 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
47 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; };
48 | 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; };
49 | 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; };
50 | 9740EEBA1CF902C7004384FC /* Flutter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Flutter.framework; path = Flutter/Flutter.framework; sourceTree = ""; };
51 | 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
52 | 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
53 | 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
54 | 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
55 | 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
56 | /* End PBXFileReference section */
57 |
58 | /* Begin PBXFrameworksBuildPhase section */
59 | 97C146EB1CF9000F007C117D /* Frameworks */ = {
60 | isa = PBXFrameworksBuildPhase;
61 | buildActionMask = 2147483647;
62 | files = (
63 | 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */,
64 | 3B80C3941E831B6300D905FE /* App.framework in Frameworks */,
65 | );
66 | runOnlyForDeploymentPostprocessing = 0;
67 | };
68 | /* End PBXFrameworksBuildPhase section */
69 |
70 | /* Begin PBXGroup section */
71 | 9740EEB11CF90186004384FC /* Flutter */ = {
72 | isa = PBXGroup;
73 | children = (
74 | 2D5378251FAA1A9400D5DBA9 /* flutter_assets */,
75 | 3B80C3931E831B6300D905FE /* App.framework */,
76 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
77 | 9740EEBA1CF902C7004384FC /* Flutter.framework */,
78 | 9740EEB21CF90195004384FC /* Debug.xcconfig */,
79 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
80 | 9740EEB31CF90195004384FC /* Generated.xcconfig */,
81 | );
82 | name = Flutter;
83 | sourceTree = "";
84 | };
85 | 97C146E51CF9000F007C117D = {
86 | isa = PBXGroup;
87 | children = (
88 | 9740EEB11CF90186004384FC /* Flutter */,
89 | 97C146F01CF9000F007C117D /* Runner */,
90 | 97C146EF1CF9000F007C117D /* Products */,
91 | );
92 | sourceTree = "";
93 | };
94 | 97C146EF1CF9000F007C117D /* Products */ = {
95 | isa = PBXGroup;
96 | children = (
97 | 97C146EE1CF9000F007C117D /* Runner.app */,
98 | );
99 | name = Products;
100 | sourceTree = "";
101 | };
102 | 97C146F01CF9000F007C117D /* Runner */ = {
103 | isa = PBXGroup;
104 | children = (
105 | 97C146FA1CF9000F007C117D /* Main.storyboard */,
106 | 97C146FD1CF9000F007C117D /* Assets.xcassets */,
107 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
108 | 97C147021CF9000F007C117D /* Info.plist */,
109 | 97C146F11CF9000F007C117D /* Supporting Files */,
110 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
111 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
112 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
113 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
114 | );
115 | path = Runner;
116 | sourceTree = "";
117 | };
118 | 97C146F11CF9000F007C117D /* Supporting Files */ = {
119 | isa = PBXGroup;
120 | children = (
121 | );
122 | name = "Supporting Files";
123 | sourceTree = "";
124 | };
125 | /* End PBXGroup section */
126 |
127 | /* Begin PBXNativeTarget section */
128 | 97C146ED1CF9000F007C117D /* Runner */ = {
129 | isa = PBXNativeTarget;
130 | buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
131 | buildPhases = (
132 | 9740EEB61CF901F6004384FC /* Run Script */,
133 | 97C146EA1CF9000F007C117D /* Sources */,
134 | 97C146EB1CF9000F007C117D /* Frameworks */,
135 | 97C146EC1CF9000F007C117D /* Resources */,
136 | 9705A1C41CF9048500538489 /* Embed Frameworks */,
137 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */,
138 | );
139 | buildRules = (
140 | );
141 | dependencies = (
142 | );
143 | name = Runner;
144 | productName = Runner;
145 | productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
146 | productType = "com.apple.product-type.application";
147 | };
148 | /* End PBXNativeTarget section */
149 |
150 | /* Begin PBXProject section */
151 | 97C146E61CF9000F007C117D /* Project object */ = {
152 | isa = PBXProject;
153 | attributes = {
154 | LastUpgradeCheck = 0910;
155 | ORGANIZATIONNAME = "The Chromium Authors";
156 | TargetAttributes = {
157 | 97C146ED1CF9000F007C117D = {
158 | CreatedOnToolsVersion = 7.3.1;
159 | LastSwiftMigration = 0910;
160 | };
161 | };
162 | };
163 | buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
164 | compatibilityVersion = "Xcode 3.2";
165 | developmentRegion = English;
166 | hasScannedForEncodings = 0;
167 | knownRegions = (
168 | en,
169 | Base,
170 | );
171 | mainGroup = 97C146E51CF9000F007C117D;
172 | productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
173 | projectDirPath = "";
174 | projectRoot = "";
175 | targets = (
176 | 97C146ED1CF9000F007C117D /* Runner */,
177 | );
178 | };
179 | /* End PBXProject section */
180 |
181 | /* Begin PBXResourcesBuildPhase section */
182 | 97C146EC1CF9000F007C117D /* Resources */ = {
183 | isa = PBXResourcesBuildPhase;
184 | buildActionMask = 2147483647;
185 | files = (
186 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
187 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
188 | 9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */,
189 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
190 | 2D5378261FAA1A9400D5DBA9 /* flutter_assets in Resources */,
191 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
192 | );
193 | runOnlyForDeploymentPostprocessing = 0;
194 | };
195 | /* End PBXResourcesBuildPhase section */
196 |
197 | /* Begin PBXShellScriptBuildPhase section */
198 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
199 | isa = PBXShellScriptBuildPhase;
200 | buildActionMask = 2147483647;
201 | files = (
202 | );
203 | inputPaths = (
204 | );
205 | name = "Thin Binary";
206 | outputPaths = (
207 | );
208 | runOnlyForDeploymentPostprocessing = 0;
209 | shellPath = /bin/sh;
210 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" thin";
211 | };
212 | 9740EEB61CF901F6004384FC /* Run Script */ = {
213 | isa = PBXShellScriptBuildPhase;
214 | buildActionMask = 2147483647;
215 | files = (
216 | );
217 | inputPaths = (
218 | );
219 | name = "Run Script";
220 | outputPaths = (
221 | );
222 | runOnlyForDeploymentPostprocessing = 0;
223 | shellPath = /bin/sh;
224 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
225 | };
226 | /* End PBXShellScriptBuildPhase section */
227 |
228 | /* Begin PBXSourcesBuildPhase section */
229 | 97C146EA1CF9000F007C117D /* Sources */ = {
230 | isa = PBXSourcesBuildPhase;
231 | buildActionMask = 2147483647;
232 | files = (
233 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
234 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
235 | );
236 | runOnlyForDeploymentPostprocessing = 0;
237 | };
238 | /* End PBXSourcesBuildPhase section */
239 |
240 | /* Begin PBXVariantGroup section */
241 | 97C146FA1CF9000F007C117D /* Main.storyboard */ = {
242 | isa = PBXVariantGroup;
243 | children = (
244 | 97C146FB1CF9000F007C117D /* Base */,
245 | );
246 | name = Main.storyboard;
247 | sourceTree = "";
248 | };
249 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
250 | isa = PBXVariantGroup;
251 | children = (
252 | 97C147001CF9000F007C117D /* Base */,
253 | );
254 | name = LaunchScreen.storyboard;
255 | sourceTree = "";
256 | };
257 | /* End PBXVariantGroup section */
258 |
259 | /* Begin XCBuildConfiguration section */
260 | 97C147031CF9000F007C117D /* Debug */ = {
261 | isa = XCBuildConfiguration;
262 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
263 | buildSettings = {
264 | ALWAYS_SEARCH_USER_PATHS = NO;
265 | CLANG_ANALYZER_NONNULL = YES;
266 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
267 | CLANG_CXX_LIBRARY = "libc++";
268 | CLANG_ENABLE_MODULES = YES;
269 | CLANG_ENABLE_OBJC_ARC = YES;
270 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
271 | CLANG_WARN_BOOL_CONVERSION = YES;
272 | CLANG_WARN_COMMA = YES;
273 | CLANG_WARN_CONSTANT_CONVERSION = YES;
274 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
275 | CLANG_WARN_EMPTY_BODY = YES;
276 | CLANG_WARN_ENUM_CONVERSION = YES;
277 | CLANG_WARN_INFINITE_RECURSION = YES;
278 | CLANG_WARN_INT_CONVERSION = YES;
279 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
280 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
281 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
282 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
283 | CLANG_WARN_STRICT_PROTOTYPES = YES;
284 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
285 | CLANG_WARN_UNREACHABLE_CODE = YES;
286 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
287 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
288 | COPY_PHASE_STRIP = NO;
289 | DEBUG_INFORMATION_FORMAT = dwarf;
290 | ENABLE_STRICT_OBJC_MSGSEND = YES;
291 | ENABLE_TESTABILITY = YES;
292 | GCC_C_LANGUAGE_STANDARD = gnu99;
293 | GCC_DYNAMIC_NO_PIC = NO;
294 | GCC_NO_COMMON_BLOCKS = YES;
295 | GCC_OPTIMIZATION_LEVEL = 0;
296 | GCC_PREPROCESSOR_DEFINITIONS = (
297 | "DEBUG=1",
298 | "$(inherited)",
299 | );
300 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
301 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
302 | GCC_WARN_UNDECLARED_SELECTOR = YES;
303 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
304 | GCC_WARN_UNUSED_FUNCTION = YES;
305 | GCC_WARN_UNUSED_VARIABLE = YES;
306 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
307 | MTL_ENABLE_DEBUG_INFO = YES;
308 | ONLY_ACTIVE_ARCH = YES;
309 | SDKROOT = iphoneos;
310 | TARGETED_DEVICE_FAMILY = "1,2";
311 | };
312 | name = Debug;
313 | };
314 | 97C147041CF9000F007C117D /* Release */ = {
315 | isa = XCBuildConfiguration;
316 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
317 | buildSettings = {
318 | ALWAYS_SEARCH_USER_PATHS = NO;
319 | CLANG_ANALYZER_NONNULL = YES;
320 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
321 | CLANG_CXX_LIBRARY = "libc++";
322 | CLANG_ENABLE_MODULES = YES;
323 | CLANG_ENABLE_OBJC_ARC = YES;
324 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
325 | CLANG_WARN_BOOL_CONVERSION = YES;
326 | CLANG_WARN_COMMA = YES;
327 | CLANG_WARN_CONSTANT_CONVERSION = YES;
328 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
329 | CLANG_WARN_EMPTY_BODY = YES;
330 | CLANG_WARN_ENUM_CONVERSION = YES;
331 | CLANG_WARN_INFINITE_RECURSION = YES;
332 | CLANG_WARN_INT_CONVERSION = YES;
333 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
334 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
335 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
336 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
337 | CLANG_WARN_STRICT_PROTOTYPES = YES;
338 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
339 | CLANG_WARN_UNREACHABLE_CODE = YES;
340 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
341 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
342 | COPY_PHASE_STRIP = NO;
343 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
344 | ENABLE_NS_ASSERTIONS = NO;
345 | ENABLE_STRICT_OBJC_MSGSEND = YES;
346 | GCC_C_LANGUAGE_STANDARD = gnu99;
347 | GCC_NO_COMMON_BLOCKS = YES;
348 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
349 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
350 | GCC_WARN_UNDECLARED_SELECTOR = YES;
351 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
352 | GCC_WARN_UNUSED_FUNCTION = YES;
353 | GCC_WARN_UNUSED_VARIABLE = YES;
354 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
355 | MTL_ENABLE_DEBUG_INFO = NO;
356 | SDKROOT = iphoneos;
357 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
358 | TARGETED_DEVICE_FAMILY = "1,2";
359 | VALIDATE_PRODUCT = YES;
360 | };
361 | name = Release;
362 | };
363 | 97C147061CF9000F007C117D /* Debug */ = {
364 | isa = XCBuildConfiguration;
365 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
366 | buildSettings = {
367 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
368 | CLANG_ENABLE_MODULES = YES;
369 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
370 | ENABLE_BITCODE = NO;
371 | FRAMEWORK_SEARCH_PATHS = (
372 | "$(inherited)",
373 | "$(PROJECT_DIR)/Flutter",
374 | );
375 | INFOPLIST_FILE = Runner/Info.plist;
376 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
377 | LIBRARY_SEARCH_PATHS = (
378 | "$(inherited)",
379 | "$(PROJECT_DIR)/Flutter",
380 | );
381 | PRODUCT_BUNDLE_IDENTIFIER = im.juejin.juejinFlutter;
382 | PRODUCT_NAME = "$(TARGET_NAME)";
383 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
384 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
385 | SWIFT_SWIFT3_OBJC_INFERENCE = On;
386 | SWIFT_VERSION = 4.0;
387 | VERSIONING_SYSTEM = "apple-generic";
388 | };
389 | name = Debug;
390 | };
391 | 97C147071CF9000F007C117D /* Release */ = {
392 | isa = XCBuildConfiguration;
393 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
394 | buildSettings = {
395 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
396 | CLANG_ENABLE_MODULES = YES;
397 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
398 | ENABLE_BITCODE = NO;
399 | FRAMEWORK_SEARCH_PATHS = (
400 | "$(inherited)",
401 | "$(PROJECT_DIR)/Flutter",
402 | );
403 | INFOPLIST_FILE = Runner/Info.plist;
404 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
405 | LIBRARY_SEARCH_PATHS = (
406 | "$(inherited)",
407 | "$(PROJECT_DIR)/Flutter",
408 | );
409 | PRODUCT_BUNDLE_IDENTIFIER = im.juejin.juejinFlutter;
410 | PRODUCT_NAME = "$(TARGET_NAME)";
411 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
412 | SWIFT_SWIFT3_OBJC_INFERENCE = On;
413 | SWIFT_VERSION = 4.0;
414 | VERSIONING_SYSTEM = "apple-generic";
415 | };
416 | name = Release;
417 | };
418 | /* End XCBuildConfiguration section */
419 |
420 | /* Begin XCConfigurationList section */
421 | 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
422 | isa = XCConfigurationList;
423 | buildConfigurations = (
424 | 97C147031CF9000F007C117D /* Debug */,
425 | 97C147041CF9000F007C117D /* Release */,
426 | );
427 | defaultConfigurationIsVisible = 0;
428 | defaultConfigurationName = Release;
429 | };
430 | 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
431 | isa = XCConfigurationList;
432 | buildConfigurations = (
433 | 97C147061CF9000F007C117D /* Debug */,
434 | 97C147071CF9000F007C117D /* Release */,
435 | );
436 | defaultConfigurationIsVisible = 0;
437 | defaultConfigurationName = Release;
438 | };
439 | /* End XCConfigurationList section */
440 | };
441 | rootObject = 97C146E61CF9000F007C117D /* Project object */;
442 | }
443 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
31 |
32 |
33 |
34 |
40 |
41 |
42 |
43 |
44 |
45 |
56 |
58 |
64 |
65 |
66 |
67 |
68 |
69 |
75 |
77 |
83 |
84 |
85 |
86 |
88 |
89 |
92 |
93 |
94 |
--------------------------------------------------------------------------------
/ios/Runner.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | BuildSystemType
6 | Original
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ios/Runner/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 | import Flutter
3 |
4 | @UIApplicationMain
5 | @objc class AppDelegate: FlutterAppDelegate {
6 | override func application(
7 | _ application: UIApplication,
8 | didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?
9 | ) -> Bool {
10 | GeneratedPluginRegistrant.register(with: self)
11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions)
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "size" : "20x20",
5 | "idiom" : "iphone",
6 | "filename" : "Icon-App-20x20@2x.png",
7 | "scale" : "2x"
8 | },
9 | {
10 | "size" : "20x20",
11 | "idiom" : "iphone",
12 | "filename" : "Icon-App-20x20@3x.png",
13 | "scale" : "3x"
14 | },
15 | {
16 | "size" : "29x29",
17 | "idiom" : "iphone",
18 | "filename" : "Icon-App-29x29@1x.png",
19 | "scale" : "1x"
20 | },
21 | {
22 | "size" : "29x29",
23 | "idiom" : "iphone",
24 | "filename" : "Icon-App-29x29@2x.png",
25 | "scale" : "2x"
26 | },
27 | {
28 | "size" : "29x29",
29 | "idiom" : "iphone",
30 | "filename" : "Icon-App-29x29@3x.png",
31 | "scale" : "3x"
32 | },
33 | {
34 | "size" : "40x40",
35 | "idiom" : "iphone",
36 | "filename" : "Icon-App-40x40@2x.png",
37 | "scale" : "2x"
38 | },
39 | {
40 | "size" : "40x40",
41 | "idiom" : "iphone",
42 | "filename" : "Icon-App-40x40@3x.png",
43 | "scale" : "3x"
44 | },
45 | {
46 | "size" : "60x60",
47 | "idiom" : "iphone",
48 | "filename" : "Icon-App-60x60@2x.png",
49 | "scale" : "2x"
50 | },
51 | {
52 | "size" : "60x60",
53 | "idiom" : "iphone",
54 | "filename" : "Icon-App-60x60@3x.png",
55 | "scale" : "3x"
56 | },
57 | {
58 | "size" : "20x20",
59 | "idiom" : "ipad",
60 | "filename" : "Icon-App-20x20@1x.png",
61 | "scale" : "1x"
62 | },
63 | {
64 | "size" : "20x20",
65 | "idiom" : "ipad",
66 | "filename" : "Icon-App-20x20@2x.png",
67 | "scale" : "2x"
68 | },
69 | {
70 | "size" : "29x29",
71 | "idiom" : "ipad",
72 | "filename" : "Icon-App-29x29@1x.png",
73 | "scale" : "1x"
74 | },
75 | {
76 | "size" : "29x29",
77 | "idiom" : "ipad",
78 | "filename" : "Icon-App-29x29@2x.png",
79 | "scale" : "2x"
80 | },
81 | {
82 | "size" : "40x40",
83 | "idiom" : "ipad",
84 | "filename" : "Icon-App-40x40@1x.png",
85 | "scale" : "1x"
86 | },
87 | {
88 | "size" : "40x40",
89 | "idiom" : "ipad",
90 | "filename" : "Icon-App-40x40@2x.png",
91 | "scale" : "2x"
92 | },
93 | {
94 | "size" : "76x76",
95 | "idiom" : "ipad",
96 | "filename" : "Icon-App-76x76@1x.png",
97 | "scale" : "1x"
98 | },
99 | {
100 | "size" : "76x76",
101 | "idiom" : "ipad",
102 | "filename" : "Icon-App-76x76@2x.png",
103 | "scale" : "2x"
104 | },
105 | {
106 | "size" : "83.5x83.5",
107 | "idiom" : "ipad",
108 | "filename" : "Icon-App-83.5x83.5@2x.png",
109 | "scale" : "2x"
110 | },
111 | {
112 | "size" : "1024x1024",
113 | "idiom" : "ios-marketing",
114 | "filename" : "Icon-App-1024x1024@1x.png",
115 | "scale" : "1x"
116 | }
117 | ],
118 | "info" : {
119 | "version" : 1,
120 | "author" : "xcode"
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hanks-zyh/juejin_flutter/5dcef5b1ec5e084f3dc60350e5fd9d7309f4742b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hanks-zyh/juejin_flutter/5dcef5b1ec5e084f3dc60350e5fd9d7309f4742b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hanks-zyh/juejin_flutter/5dcef5b1ec5e084f3dc60350e5fd9d7309f4742b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hanks-zyh/juejin_flutter/5dcef5b1ec5e084f3dc60350e5fd9d7309f4742b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hanks-zyh/juejin_flutter/5dcef5b1ec5e084f3dc60350e5fd9d7309f4742b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hanks-zyh/juejin_flutter/5dcef5b1ec5e084f3dc60350e5fd9d7309f4742b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hanks-zyh/juejin_flutter/5dcef5b1ec5e084f3dc60350e5fd9d7309f4742b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hanks-zyh/juejin_flutter/5dcef5b1ec5e084f3dc60350e5fd9d7309f4742b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hanks-zyh/juejin_flutter/5dcef5b1ec5e084f3dc60350e5fd9d7309f4742b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hanks-zyh/juejin_flutter/5dcef5b1ec5e084f3dc60350e5fd9d7309f4742b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hanks-zyh/juejin_flutter/5dcef5b1ec5e084f3dc60350e5fd9d7309f4742b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hanks-zyh/juejin_flutter/5dcef5b1ec5e084f3dc60350e5fd9d7309f4742b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hanks-zyh/juejin_flutter/5dcef5b1ec5e084f3dc60350e5fd9d7309f4742b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hanks-zyh/juejin_flutter/5dcef5b1ec5e084f3dc60350e5fd9d7309f4742b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hanks-zyh/juejin_flutter/5dcef5b1ec5e084f3dc60350e5fd9d7309f4742b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "LaunchImage.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "LaunchImage@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "LaunchImage@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hanks-zyh/juejin_flutter/5dcef5b1ec5e084f3dc60350e5fd9d7309f4742b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hanks-zyh/juejin_flutter/5dcef5b1ec5e084f3dc60350e5fd9d7309f4742b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hanks-zyh/juejin_flutter/5dcef5b1ec5e084f3dc60350e5fd9d7309f4742b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md:
--------------------------------------------------------------------------------
1 | # Launch Screen Assets
2 |
3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory.
4 |
5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.
--------------------------------------------------------------------------------
/ios/Runner/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/ios/Runner/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/ios/Runner/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | juejin_flutter
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | $(FLUTTER_BUILD_NAME)
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | $(FLUTTER_BUILD_NUMBER)
23 | LSRequiresIPhoneOS
24 |
25 | UILaunchStoryboardName
26 | LaunchScreen
27 | UIMainStoryboardFile
28 | Main
29 | UISupportedInterfaceOrientations
30 |
31 | UIInterfaceOrientationPortrait
32 | UIInterfaceOrientationLandscapeLeft
33 | UIInterfaceOrientationLandscapeRight
34 |
35 | UISupportedInterfaceOrientations~ipad
36 |
37 | UIInterfaceOrientationPortrait
38 | UIInterfaceOrientationPortraitUpsideDown
39 | UIInterfaceOrientationLandscapeLeft
40 | UIInterfaceOrientationLandscapeRight
41 |
42 | UIViewControllerBasedStatusBarAppearance
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/ios/Runner/Runner-Bridging-Header.h:
--------------------------------------------------------------------------------
1 | #import "GeneratedPluginRegistrant.h"
--------------------------------------------------------------------------------
/lib/config/config_color.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class ConfigColor {
4 | static bool darkTheme = false;
5 | static ThemeData themeData = new ThemeData.light();
6 | static Color colorPrimary = Color(0xFF007FFF);
7 | static Color colorPrimaryDark = Color(0xFF0072E5);
8 | static Color colorPrimaryAccent = Color(0xFF007FFF);
9 | static Color colorText1 = Color(0xFF333333);
10 | static Color colorText2 = Color(0xFF67727B);
11 | static Color colorText3 = Color(0xFF8A9AA9);
12 | static Color colorWindowBackground = Color(0xFFF4F6F9);
13 | static Color colorContentBackground = Color(0xFFFFFFFF);
14 | static Color colorDivider = Color(0xFFF1F1F1);
15 | }
--------------------------------------------------------------------------------
/lib/main.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter/rendering.dart';
3 | import 'package:juejin_flutter/config/config_color.dart';
4 | import 'package:juejin_flutter/page/logoPage.dart';
5 | import 'package:juejin_flutter/page/mainPage.dart';
6 |
7 | void main(){
8 | // debugPaintSizeEnabled = true;
9 | runApp(MaterialApp(
10 | theme: ConfigColor.themeData,
11 | home: MainPage(),
12 | ));
13 |
14 | }
15 |
--------------------------------------------------------------------------------
/lib/model/category.dart:
--------------------------------------------------------------------------------
1 |
2 | class Category {
3 | String id;
4 | String name;
5 | String title;
6 |
7 | Category();
8 | }
--------------------------------------------------------------------------------
/lib/model/entry.dart:
--------------------------------------------------------------------------------
1 | import 'package:juejin_flutter/model/category.dart';
2 | import 'package:juejin_flutter/model/tag.dart';
3 | import 'package:juejin_flutter/model/user.dart';
4 |
5 | class Entry {
6 | int commentsCount;
7 | int collectionCount;
8 | String objectId;
9 | String title;
10 | String content;
11 | String originalUrl;
12 | String screenshot;
13 | String type;
14 | String createdAt;
15 | User user;
16 | Category category;
17 | List tags;
18 |
19 | Entry();
20 |
21 | factory Entry.fromJson(Map json) {
22 | var entry = Entry();
23 | entry.objectId = json['objectId'];
24 | entry.title = json['title'];
25 | entry.content = json['content'];
26 | entry.originalUrl = json['originalUrl'];
27 | entry.type = json['type'];
28 | entry.createdAt = json['createdAt'];
29 | entry.screenshot = json['screenshot'];
30 | entry.commentsCount = json['commentsCount'];
31 | entry.collectionCount = json['collectionCount'];
32 |
33 | var user = User();
34 | var jsonUser = json['user'];
35 | user.objectId = jsonUser['objectId'];
36 | user.avatarLarge = jsonUser['avatarLarge'];
37 | user.username = jsonUser['username'];
38 | user.jobTitle = jsonUser['jobTitle'];
39 | user.company = jsonUser['company'];
40 | entry.user = user;
41 |
42 | var category = Category();
43 | var categoryJson = json['category'];
44 | category.id = categoryJson['id'];
45 | category.name = categoryJson['name'];
46 | category.title = categoryJson['title'];
47 |
48 | entry.category = category;
49 | entry.tags = List();
50 | for(var tagJson in json['tags']){
51 | entry.tags.add(Tag.formJson(tagJson));
52 | }
53 |
54 | return entry;
55 | }
56 | }
--------------------------------------------------------------------------------
/lib/model/pin.dart:
--------------------------------------------------------------------------------
1 |
2 | import 'package:juejin_flutter/model/topic.dart';
3 | import 'package:juejin_flutter/model/user.dart';
4 |
5 | class Pin {
6 | int commentCount;
7 | int likedCount;
8 | bool isLiked;
9 | String objectId;
10 | String title;
11 | String content;
12 | String url;
13 | String urlTitle;
14 | String urlPic;
15 | List pictures;
16 | String createdAt;
17 | User user;
18 | Topic topic;
19 |
20 | Pin();
21 |
22 | factory Pin.fromJson(Map json) {
23 | var pin = Pin();
24 | pin.objectId = json['objectId'];
25 | pin.title = json['title'];
26 | pin.content = json['content'];
27 | pin.commentCount = json['commentCount'];
28 | pin.likedCount = json['likedCount'];
29 | pin.createdAt = json['createdAt'];
30 | pin.url = json['url'];
31 | pin.urlTitle = json['urlTitle'];
32 | pin.urlPic = json['urlPic'];
33 | pin.pictures = List();
34 | for (var value in json['pictures']) {
35 | pin.pictures.add(value);
36 | }
37 | pin.user = User.fromJson(json['user']);
38 | pin.topic = Topic.fromJson(json['topic']);
39 | return pin;
40 | }
41 |
42 | @override
43 | String toString() {
44 | return 'Pin{commentCount: $commentCount, likedCount: $likedCount, isLiked: $isLiked, objectId: $objectId, title: $title, content: $content, url: $url, urlTitle: $urlTitle, urlPic: $urlPic, pictures: $pictures, createdAt: $createdAt, user: $user, topic: $topic}';
45 | }
46 |
47 | }
--------------------------------------------------------------------------------
/lib/model/tag.dart:
--------------------------------------------------------------------------------
1 | class Tag{
2 | Tag();
3 | String id;
4 | String title;
5 |
6 | factory Tag.formJson(Map json){
7 | Tag tag = Tag();
8 | tag.id = json['id'];
9 | tag.title = json['title'];
10 | return tag;
11 | }
12 | }
--------------------------------------------------------------------------------
/lib/model/topic.dart:
--------------------------------------------------------------------------------
1 | class Topic {
2 | int msgsCount;
3 | int followersCount;
4 | int attendersCount;
5 | String icon;
6 | String description;
7 | String title;
8 | String objectId;
9 | String createdAt;
10 | String updatedAt;
11 | String latestMsgCreatedAt;
12 |
13 | Topic();
14 |
15 | factory Topic.fromJson(Map json) {
16 | if (json == null) return null;
17 | var topic = Topic();
18 | topic.objectId = json['objectId'];
19 | topic.msgsCount = json['msgsCount'];
20 | topic.followersCount = json['followersCount'];
21 | topic.attendersCount = json['attendersCount'];
22 | topic.icon = json['icon'];
23 | topic.description = json['description'];
24 | topic.title = json['title'];
25 | topic.objectId = json['objectId'];
26 | topic.createdAt = json['createdAt'];
27 | topic.updatedAt = json['updatedAt'];
28 | topic.latestMsgCreatedAt = json['latestMsgCreatedAt'];
29 | return topic;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/lib/model/user.dart:
--------------------------------------------------------------------------------
1 | class User {
2 | String avatarLarge;
3 | String username;
4 | String jobTitle;
5 | String company;
6 | String objectId;
7 | User();
8 |
9 | factory User.fromJson(Map jsonUser) {
10 | if (jsonUser == null) return null;
11 | var user = User();
12 | user.objectId = jsonUser['objectId'];
13 | user.avatarLarge = jsonUser['avatarLarge'];
14 | user.username = jsonUser['username'];
15 | user.jobTitle = jsonUser['jobTitle'];
16 | user.company = jsonUser['company'];
17 | return user;
18 | }
19 | }
--------------------------------------------------------------------------------
/lib/model/xiaoce.dart:
--------------------------------------------------------------------------------
1 |
2 | import 'package:juejin_flutter/model/user.dart';
3 |
4 | class Xiaoce {
5 | int buyCount;
6 | double price;
7 | String id;
8 | String title;
9 | String desc;
10 | String img;
11 | String createdAt;
12 | User userData;
13 |
14 | Xiaoce();
15 |
16 | factory Xiaoce.fromJson(Map json) {
17 | var xiaoce = Xiaoce();
18 | xiaoce.id = json['id'];
19 | xiaoce.title = json['title'];
20 | xiaoce.desc = json['desc'];
21 | xiaoce.img = json['img'];
22 | xiaoce.buyCount = json['buyCount'];
23 | xiaoce.createdAt = json['createdAt'];
24 | xiaoce.price = json['price'];
25 |
26 | var user = User();
27 | var jsonUser = json['userData'];
28 | user.objectId = jsonUser['objectId'];
29 | user.avatarLarge = jsonUser['avatarLarge'];
30 | user.username = jsonUser['username'];
31 | user.jobTitle = jsonUser['jobTitle'];
32 | user.company = jsonUser['company'];
33 | xiaoce.userData = user;
34 |
35 | return xiaoce;
36 | }
37 | }
--------------------------------------------------------------------------------
/lib/page/explorePage.dart:
--------------------------------------------------------------------------------
1 | import 'dart:convert';
2 |
3 | import 'package:flutter/material.dart';
4 | import 'package:http/http.dart' as http;
5 | import 'package:juejin_flutter/config/config_color.dart';
6 | import 'package:juejin_flutter/model/entry.dart';
7 | import 'package:juejin_flutter/page/webpage.dart';
8 | import 'package:juejin_flutter/widget/banner/banner_evalutor.dart';
9 | import 'package:juejin_flutter/widget/banner/banner_widget.dart';
10 |
11 | Future> fetchPost() async {
12 | final response = await http.get(
13 | 'https://timeline-merger-ms.juejin.im/v1/get_entry_by_rank?src=web&before=15.838909742541&limit=20&category=all');
14 |
15 | if (response.statusCode == 200) {
16 | // If the call to the server was successful, parse the JSON
17 | var decode = json.decode(response.body);
18 | var entrylist = decode['d']['entrylist'];
19 | var list = List();
20 | for (var value in entrylist) {
21 | try {
22 | var entry = Entry.fromJson(value);
23 | list.add(entry);
24 | } catch (e) {
25 | print(e);
26 | }
27 | }
28 | return list;
29 | } else {
30 | // If that call was not successful, throw an error.
31 | throw Exception('Failed to load post');
32 | }
33 | }
34 |
35 | class ExplorePage extends StatefulWidget {
36 | @override
37 | State createState() {
38 | return _ExplorePageState();
39 | }
40 | }
41 |
42 | class _ExplorePageState extends State
43 | with AutomaticKeepAliveClientMixin {
44 | @override
45 | bool get wantKeepAlive => true;
46 |
47 | List items = List();
48 | ScrollController _scrollController = ScrollController();
49 |
50 | @override
51 | void initState() {
52 | _onRefresh();
53 | _scrollController.addListener(() {
54 | if (_scrollController.position.pixels ==
55 | _scrollController.position.maxScrollExtent) {
56 | print("loadMore");
57 | _getMoreData();
58 | }
59 | });
60 | super.initState();
61 | }
62 |
63 | @override
64 | Widget build(BuildContext context) {
65 | return MaterialApp(
66 | home: Scaffold(
67 | appBar: AppBar(
68 | title: Container(
69 | width: double.infinity,
70 | height: 36.0,
71 | padding: EdgeInsets.only(left: 12.0, right: 12.0),
72 | decoration: BoxDecoration(
73 | color: Colors.white24,
74 | borderRadius: BorderRadius.all(Radius.circular(3.0))),
75 | child: Row(children: [
76 | Icon(
77 | Icons.search,
78 | color: Colors.white,
79 | size: 20.0,
80 | ),
81 | Container(
82 | child: Text(
83 | "搜索",
84 | style: TextStyle(color: Colors.white30, fontSize: 14.0),
85 | ),
86 | margin: EdgeInsets.only(left: 8.0),
87 | )
88 | ]),
89 | ),
90 | backgroundColor: ConfigColor.colorPrimary,
91 | elevation: 1.0,
92 | ),
93 | body: RefreshIndicator(
94 | onRefresh: _onRefresh,
95 | child: ListView.builder(
96 | itemCount: items.length <= 0 ? 0 : items.length + 2,
97 | itemBuilder: (context, index) {
98 | if (index == 0) {
99 | return _buildExploreHeader();
100 | } else if (index == items.length + 1) {
101 | return _buildProgressIndicator();
102 | } else {
103 | var i = index - 1;
104 | if (i >= 0 && i < items.length) {
105 | final item = items[i];
106 | return getItemView(item);
107 | } else {
108 | return _buildProgressIndicator();
109 | }
110 | }
111 | },
112 | controller: _scrollController,
113 | ),
114 | ),
115 | ),
116 | );
117 | }
118 |
119 | Widget _buildProgressIndicator() {
120 | return Container(
121 | margin: EdgeInsets.all(16.0),
122 | child: Center(
123 | child: SizedBox(
124 | child: CircularProgressIndicator(
125 | strokeWidth: 2.0,
126 | ),
127 | height: 20.0,
128 | width: 20.0,
129 | ),
130 | ));
131 | }
132 |
133 | Widget _buildExploreHeader() {
134 | final List data = [
135 | new Model(
136 | imgUrl:
137 | 'https://user-gold-cdn.xitu.io/2018/11/8/166f3b31f23e06b5?imageView2/1/w/1304/h/734/q/85/format/webp/interlace/1'),
138 | new Model(
139 | imgUrl:
140 | 'https://user-gold-cdn.xitu.io/2018/11/6/166e6521376a2858?imageView2/1/w/1304/h/734/q/85/format/webp/interlace/1'),
141 | new Model(
142 | imgUrl:
143 | 'https://user-gold-cdn.xitu.io/2018/11/7/166ee89218c82bc4?imageView2/1/w/1304/h/734/q/85/format/webp/interlace/1'),
144 | ];
145 | return Column(
146 | children: [
147 | BannerWidget(
148 | height: 160,
149 | data: data,
150 | curve: Curves.decelerate,
151 | duration: 520,
152 | ),
153 | Container(
154 | height: 84.0,
155 | color: ConfigColor.colorContentBackground,
156 | child: Row(
157 | children: [
158 | Expanded(
159 | child: FlatButton(
160 | onPressed: () => {},
161 | padding: EdgeInsets.only(top: 16.0),
162 | child: Column(
163 | children: [
164 | Icon(
165 | Icons.whatshot,
166 | color: Color(0xFFFF5E35),
167 | size: 36.0,
168 | ),
169 | Text(
170 | "本周最热",
171 | style: TextStyle(
172 | color: ConfigColor.colorText1,
173 | fontSize: 12.0,
174 | ),
175 | )
176 | ],
177 | ))),
178 | Expanded(
179 | child: FlatButton(
180 | padding: EdgeInsets.only(top: 16.0),
181 | onPressed: () => {},
182 | child: Column(
183 | children: [
184 | Icon(Icons.confirmation_number,
185 | color: Color(0xFF18C09A), size: 36.0),
186 | Text(
187 | "收藏集合",
188 | style: TextStyle(
189 | color: ConfigColor.colorText1, fontSize: 12.0),
190 | )
191 | ],
192 | ))),
193 | Expanded(
194 | child: FlatButton(
195 | padding: EdgeInsets.only(top: 16.0),
196 | onPressed: () => {},
197 | child: Column(
198 | children: [
199 | Icon(Icons.notifications,
200 | color: Color(0xFFFFCC00), size: 36.0),
201 | Text(
202 | "活动",
203 | style: TextStyle(
204 | color: ConfigColor.colorText1, fontSize: 12.0),
205 | )
206 | ],
207 | ))),
208 | ],
209 | ),
210 | ),
211 | Container(
212 | margin: EdgeInsets.only(top: 8.0),
213 | padding: EdgeInsets.only(left: 16.0, right: 8.0),
214 | height: 40.0,
215 | color: ConfigColor.colorContentBackground,
216 | child: Row(
217 | mainAxisAlignment: MainAxisAlignment.center,
218 | children: [
219 | Container(
220 | child: Icon(
221 | Icons.whatshot,
222 | color: Color(0xFFFF5E35),
223 | size: 16.0,
224 | ),
225 | margin: EdgeInsets.only(right: 8.0),
226 | ),
227 | Expanded(
228 | child: Text(
229 | "本周最热",
230 | style: TextStyle(
231 | color: ConfigColor.colorText1,
232 | fontSize: 13.0,
233 | ),
234 | )),
235 | Row(
236 | children: [
237 | Icon(Icons.settings, color: Colors.black26, size: 16.0),
238 | Container(
239 | child: Text(
240 | "定制热门",
241 | style: TextStyle(
242 | color: ConfigColor.colorText2, fontSize: 12.0),
243 | ),
244 | margin: EdgeInsets.only(left: 4.0, right: 8.0),
245 | ),
246 | ],
247 | ),
248 | ],
249 | ),
250 | ),
251 | ],
252 | );
253 | }
254 |
255 | @override
256 | void dispose() {
257 | super.dispose();
258 | _scrollController.dispose();
259 | }
260 |
261 | Widget getItemView(Entry entry) {
262 | return InkWell(
263 | onTap: () {
264 | Navigator.push(context, MaterialPageRoute(builder: (_)=> WebPage(url: entry.originalUrl,)));
265 | },
266 | child: Container(
267 | padding:
268 | EdgeInsets.only(top: 16.0, right: 16.0, bottom: 16.0, left: 16.0),
269 | decoration: BoxDecoration(
270 | color: ConfigColor.colorContentBackground,
271 | border: Border(
272 | top: BorderSide(color: ConfigColor.colorDivider, width: 0.5)),
273 | ),
274 | child: Row(children: [
275 | Expanded(
276 | child: Column(
277 | crossAxisAlignment: CrossAxisAlignment.start,
278 | children: [
279 | Container(
280 | padding: EdgeInsets.only(right: 16.0),
281 | child: Text(
282 | entry.title.toUpperCase(),
283 | maxLines: 2,
284 | textAlign: TextAlign.left,
285 | style: TextStyle(
286 | fontSize: 14.0,
287 | height: 1.1,
288 | color: ConfigColor.colorText1),
289 | ),
290 | ),
291 | Container(
292 | child: Text(
293 | entry.user.username,
294 | maxLines: 1,
295 | style: TextStyle(
296 | fontSize: 10.0, color: ConfigColor.colorText2),
297 | ),
298 | margin: EdgeInsets.only(top: 8.0),
299 | ),
300 | ],
301 | )),
302 | getScreenshotWidget(entry.screenshot)
303 | ])),
304 | );
305 | }
306 |
307 | Widget getScreenshotWidget(String url) {
308 | if (url == null || url.isEmpty) return Container();
309 | return Container(
310 | height: 60.0,
311 | width: 60.0,
312 | child: Image.network(url, fit: BoxFit.cover),
313 | );
314 | }
315 |
316 | Future _onRefresh() async {
317 | await fetchPost().then((list) {
318 | setState(() {
319 | items.clear();
320 | items.addAll(list);
321 | return null;
322 | });
323 | });
324 | }
325 |
326 | Future _getMoreData() async {
327 | await fetchPost().then((list) {
328 | setState(() {
329 | items.addAll(list);
330 | return null;
331 | });
332 | });
333 | }
334 | }
335 |
336 | class Model extends Object with BannerWithEval {
337 | final String imgUrl;
338 |
339 | Model({this.imgUrl});
340 |
341 | @override
342 | get bannerUrl => imgUrl;
343 | }
344 |
--------------------------------------------------------------------------------
/lib/page/homePage.dart:
--------------------------------------------------------------------------------
1 | import 'dart:convert';
2 |
3 | import 'package:flutter/material.dart';
4 | import 'package:http/http.dart' as http;
5 | import 'package:juejin_flutter/config/config_color.dart';
6 | import 'package:juejin_flutter/model/entry.dart';
7 | import 'package:juejin_flutter/page/webpage.dart';
8 |
9 | Future> fetchPost() async {
10 | final response = await http.get(
11 | 'https://timeline-merger-ms.juejin.im/v1/get_entry_by_rank?src=web&before=15.838909742541&limit=20&category=all');
12 |
13 | if (response.statusCode == 200) {
14 | // If the call to the server was successful, parse the JSON
15 | var decode = json.decode(response.body);
16 | var entrylist = decode['d']['entrylist'];
17 | var list = List();
18 | for (var value in entrylist) {
19 | try {
20 | var entry = Entry.fromJson(value);
21 | list.add(entry);
22 | } catch (e) {
23 | print(e);
24 | }
25 | }
26 | return list;
27 | } else {
28 | // If that call was not successful, throw an error.
29 | throw Exception('Failed to load post');
30 | }
31 | }
32 |
33 | class HomePage extends StatefulWidget {
34 | @override
35 | State createState() {
36 | return _HomePageState();
37 | }
38 | }
39 |
40 | class _HomePageState extends State
41 | with AutomaticKeepAliveClientMixin {
42 | @override
43 | bool get wantKeepAlive => true;
44 |
45 | List items = List();
46 | ScrollController _scrollController = ScrollController();
47 |
48 | @override
49 | void initState() {
50 | _onRefresh();
51 | _scrollController.addListener(() {
52 | if (_scrollController.position.pixels ==
53 | _scrollController.position.maxScrollExtent) {
54 | print("loadMore");
55 | _getMoreData();
56 | }
57 | });
58 | super.initState();
59 | }
60 |
61 | var tabs = ['首页', 'Android', 'iOS'];
62 |
63 | Widget getTabItemWidget(int index) {
64 | return Container(
65 | height: kToolbarHeight,
66 | child: Center(
67 | child: Text(
68 | tabs[index],
69 | textAlign: TextAlign.center,
70 | ),
71 | ),
72 | );
73 | }
74 |
75 | @override
76 | Widget build(BuildContext context) {
77 | var statusBarHeight = MediaQuery
78 | .of(context)
79 | .padding
80 | .top;
81 | var tabHeight = 40.0 + statusBarHeight;
82 | return MaterialApp(
83 | title: "Home",
84 | home: DefaultTabController(
85 | length: tabs.length,
86 | child: Scaffold(
87 | body: Stack(
88 | children: [
89 | Container(
90 | margin: EdgeInsets.only(top: tabHeight - 13.0),
91 | child: RefreshIndicator(
92 | onRefresh: _onRefresh,
93 | child: ListView.builder(
94 | itemCount: items.length <= 0 ? 0 : items.length + 1,
95 | itemBuilder: (context, index) {
96 | if (index == items.length) {
97 | return _buildProgressIndicator();
98 | } else {
99 | final item = items[index];
100 | return getItemView(item);
101 | }
102 | },
103 | controller: _scrollController,
104 | ),
105 | ),
106 | ),
107 | Container(
108 | decoration: BoxDecoration(
109 | color: ConfigColor.colorPrimary,
110 | boxShadow: [
111 | BoxShadow(
112 | color: Color(0x44000000),
113 | blurRadius: 3.0,
114 | offset: Offset(0.0, 1.0))
115 | ]),
116 | padding: EdgeInsets.only(top: statusBarHeight),
117 | height: tabHeight,
118 | width: double.infinity,
119 | child: TabBar(
120 | isScrollable: true,
121 | indicatorWeight: 2.5,
122 | indicatorColor: ConfigColor.colorContentBackground,
123 | tabs: [
124 | getTabItemWidget(0),
125 | getTabItemWidget(1),
126 | getTabItemWidget(2),
127 | ],
128 | ),
129 | ),
130 | ],
131 | ),
132 | ),
133 | ));
134 | }
135 |
136 | Widget _buildProgressIndicator() {
137 | return Container(
138 | margin: EdgeInsets.all(16.0),
139 | child: Center(
140 | child: SizedBox(
141 | child: CircularProgressIndicator(
142 | strokeWidth: 2.0,
143 | ),
144 | height: 20.0,
145 | width: 20.0,
146 | ),
147 | ));
148 | }
149 |
150 | @override
151 | void dispose() {
152 | super.dispose();
153 | _scrollController.dispose();
154 | }
155 |
156 | /// 创建一个平移变换
157 | /// 跳转过去查看源代码,可以看到有各种各样定义好的变换
158 | SlideTransition createTransition(
159 | Animation animation, Widget child) {
160 | return new SlideTransition(
161 | position: new Tween(
162 | begin: const Offset(1.0, 0.0),
163 | end: const Offset(0.0, 0.0),
164 | ).animate(animation),
165 | child: child, // child is the value returned by pageBuilder
166 | );
167 | }
168 |
169 | Widget getItemView(Entry entry) {
170 | return InkWell(
171 | onTap: () {
172 |
173 | // Navigator.of(context).push(new PageRouteBuilder(pageBuilder:
174 | // (BuildContext context, Animation animation,
175 | // Animation secondaryAnimation) {
176 | // return WebPage(url:entry.originalUrl);
177 | // }, transitionsBuilder: (
178 | // BuildContext context,
179 | // Animation animation,
180 | // Animation secondaryAnimation,
181 | // Widget child,
182 | // ) {
183 | // // 添加一个平移动画
184 | // return createTransition(animation, child);
185 | // }));
186 |
187 | Navigator.push(
188 | context, MaterialPageRoute(builder: (context) => WebPage(url:entry.originalUrl)));
189 | },
190 | child: Container(
191 | margin: EdgeInsets.only(top: 0.0, bottom: 10.0),
192 | child: Ink(
193 | padding: EdgeInsets.only(top: 16.0, right: 16.0, bottom: 4.0),
194 | decoration: BoxDecoration(
195 | color: ConfigColor.colorContentBackground,
196 | boxShadow: [
197 | new BoxShadow(
198 | color: Color(0x18000000),
199 | blurRadius: 1.0,
200 | offset: Offset(0.0, 0.5))
201 | ]),
202 | child:
203 | Column(
204 | crossAxisAlignment: CrossAxisAlignment.start,
205 | children: [
206 | Row(
207 | children: [
208 | Container(
209 | margin: EdgeInsets.only(left: 16.0),
210 | padding: const EdgeInsets.all(1.0),
211 | // borde width
212 | child: CircleAvatar(
213 | backgroundImage:
214 | NetworkImage(entry.user.avatarLarge),
215 | backgroundColor: ConfigColor.colorWindowBackground,
216 | ),
217 | decoration: new BoxDecoration(
218 | color: Color(0xffd7dade), // border color
219 | shape: BoxShape.circle,
220 | ),
221 | width: 24.0,
222 | height: 24.0),
223 | Expanded(
224 | child: Container(
225 | child: Text(
226 | entry.user.username,
227 | maxLines: 1,
228 | style: TextStyle(
229 | fontSize: 12.0, color: ConfigColor.colorText1),
230 | ),
231 | margin: EdgeInsets.only(left: 8.0),
232 | ),
233 | ),
234 | getTagWidget(entry),
235 | ],
236 | ),
237 | Container(
238 | child: Text(
239 | entry.title.toUpperCase(),
240 | maxLines: 2,
241 | textAlign: TextAlign.left,
242 | style: TextStyle(
243 | fontSize: 14.0,
244 | fontWeight: FontWeight.bold,
245 | color: ConfigColor.colorText1),
246 | ),
247 | padding: EdgeInsets.only(top: 8.0, left: 16.0),
248 | ),
249 | Container(
250 | child: Text(
251 | entry.content.trim(),
252 | maxLines: 3,
253 | style: TextStyle(
254 | fontSize: 12.0,
255 | color: ConfigColor.colorText2,
256 | height: 1.2),
257 | ),
258 | padding: EdgeInsets.only(top: 4.0, left: 16.0),
259 | ),
260 | Container(
261 | child: Row(children: [
262 | Row(
263 | children: [
264 | Container(
265 | padding: EdgeInsets.only(right: 4.0, left: 16.0),
266 | child: Image.asset(
267 | "assets/timeline_like_normal.png",
268 | width: 16.0,
269 | height: 16.0),
270 | ),
271 | Container(
272 | child: Text(
273 | "${entry.collectionCount}",
274 | style: TextStyle(
275 | fontSize: 12.0,
276 | color: ConfigColor.colorText3),
277 | ),
278 | ),
279 | ],
280 | ),
281 | Row(
282 | children: [
283 | Container(
284 | padding: EdgeInsets.only(right: 4.0, left: 16.0),
285 | child: Image.asset("assets/timeline_comment.png",
286 | width: 16.0, height: 16.0),
287 | ),
288 | Container(
289 | child: Text(
290 | "${entry.commentsCount}",
291 | style: TextStyle(
292 | fontSize: 12.0,
293 | color: ConfigColor.colorText3),
294 | ),
295 | ),
296 | ],
297 | ),
298 | ]),
299 | height: 40.0,
300 | )
301 | ]),
302 | )
303 | ));
304 | }
305 |
306 | Future _onRefresh() async {
307 | await fetchPost().then((list) {
308 | setState(() {
309 | items.clear();
310 | items.addAll(list);
311 | return null;
312 | });
313 | });
314 | }
315 |
316 | Future _getMoreData() async {
317 | await fetchPost().then((list) {
318 | setState(() {
319 | items.addAll(list);
320 | return null;
321 | });
322 | });
323 | }
324 | }
325 |
326 | Widget getTagWidget(Entry entry) {
327 | if (entry == null || entry.tags == null || entry.tags.isEmpty) {
328 | return Container(
329 | child: Text("nn"),
330 | );
331 | }
332 |
333 | var text = entry.tags[0].title;
334 | if (entry.tags.length > 1) {
335 | text += " / ${entry.tags[1].title}";
336 | }
337 |
338 | return Container(
339 | child: Text(
340 | text,
341 | style: TextStyle(
342 | color: ConfigColor.colorText3,
343 | fontSize: 12.0,
344 | ),
345 | ),
346 | );
347 | }
348 |
--------------------------------------------------------------------------------
/lib/page/logoPage.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:juejin_flutter/page/mainPage.dart';
3 |
4 | class LogoPage extends StatelessWidget {
5 | @override
6 | Widget build(BuildContext context) {
7 | var app = Scaffold(
8 | backgroundColor: Color(0xFFFFFFFF),
9 | body: Column(children: [
10 | Expanded(child: Image.asset(
11 | "assets/splash_bg.webp",
12 | fit: BoxFit.cover,
13 | ),flex: 1,),
14 | Container(
15 | child: Image.asset(
16 | "assets/splash_logo.png",
17 | fit: BoxFit.cover,
18 | ),
19 | height: 130.68,
20 | ),
21 | ]),
22 | );
23 | startMain(context);
24 | return app;
25 | }
26 |
27 | void startMain(BuildContext context) async {
28 | await Future.delayed(const Duration(seconds: 2));
29 | Navigator.push(
30 | context, MaterialPageRoute(builder: (context) => MainPage()));
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/lib/page/mainPage.dart:
--------------------------------------------------------------------------------
1 | // Copyright 2016 The Chromium Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style license that can be
3 | // found in the LICENSE file.
4 |
5 | import 'package:flutter/material.dart';
6 | import 'package:juejin_flutter/config/config_color.dart';
7 | import 'package:juejin_flutter/page/explorePage.dart';
8 | import 'package:juejin_flutter/page/homePage.dart';
9 | import 'package:juejin_flutter/page/pinPage.dart';
10 | import 'package:juejin_flutter/page/userPage.dart';
11 | import 'package:juejin_flutter/page/xiaocePage.dart';
12 |
13 | class MainPage extends StatefulWidget {
14 | static const String routeName = '/page/main';
15 |
16 | @override
17 | _MainPageState createState() => _MainPageState();
18 | }
19 |
20 | class _MainPageState extends State with TickerProviderStateMixin {
21 | int _tabIndex = 0;
22 | var tabImages;
23 | var appBarTitles = ['首页', '动态', '发现', '小测', '我的'];
24 |
25 | /*
26 | * 存放三个页面,跟fragmentList一样
27 | */
28 | var _pageList;
29 | var pageController;
30 |
31 | /*
32 | * 根据选择获得对应的normal或是press的icon
33 | */
34 | Widget getTabIcon(int curIndex) {
35 | var image;
36 | if (curIndex == _tabIndex) {
37 | image = tabImages[curIndex][1];
38 | } else {
39 | image = tabImages[curIndex][0];
40 | }
41 | return new Center(
42 | child: image,
43 | );
44 | }
45 |
46 | /*
47 | * 获取bottomTab的颜色和文字
48 | */
49 | Container getTabTitle(int curIndex) {
50 | return Container(
51 | width: 0.0,
52 | height: 0.0,
53 | );
54 | }
55 |
56 | /*
57 | * 根据image路径获取图片
58 | */
59 | Image getTabImage(path) {
60 | return new Image.asset(path, width: 32.0, height: 32.0);
61 | }
62 |
63 | void initData() {
64 | /*
65 | * 初始化选中和未选中的icon
66 | */
67 | tabImages = [
68 | [
69 | getTabImage('assets/tab_home_normal.png'),
70 | getTabImage('assets/tab_home.png')
71 | ],
72 | [
73 | getTabImage('assets/tab_activity_normal.png'),
74 | getTabImage('assets/tab_activity.png')
75 | ],
76 | [
77 | getTabImage('assets/tab_explore_normal.png'),
78 | getTabImage('assets/tab_explore.png')
79 | ],
80 | [
81 | getTabImage('assets/tab_xiaoce_normal.png'),
82 | getTabImage('assets/tab_xiaoce.png')
83 | ],
84 | [
85 | getTabImage('assets/tab_profile_normal.png'),
86 | getTabImage('assets/tab_profile.png')
87 | ],
88 | ];
89 | /*
90 | * 三个子界面
91 | */
92 | _pageList = [
93 | new HomePage(),
94 | new PinPage(),
95 | new ExplorePage(),
96 | new XiaocePage(),
97 | new UserPage(),
98 | ];
99 |
100 | pageController = PageController(initialPage: _tabIndex);
101 | }
102 |
103 | Widget getBottomIcon(var index) {
104 | return Expanded(
105 | child: Material(
106 | color: Colors.transparent,
107 | child: InkWell(
108 | onTap: () {
109 | setState(() {
110 | _tabIndex = index;
111 | });
112 | pageController.jumpToPage(_tabIndex);
113 | },
114 | child: getTabIcon(index)),
115 | ),
116 | flex: 1,
117 | );
118 | }
119 |
120 | @override
121 | Widget build(BuildContext context) {
122 | //初始化数据
123 | initData();
124 |
125 | return Scaffold(
126 | body: Column(
127 | children: [
128 | Expanded(
129 | child: PageView(
130 | controller: pageController,
131 | physics: NeverScrollableScrollPhysics(),
132 | children: [
133 | _pageList[0],
134 | _pageList[1],
135 | _pageList[2],
136 | _pageList[3],
137 | _pageList[4],
138 | ],
139 | ),
140 | flex: 1,
141 | ),
142 | Container(
143 | decoration: BoxDecoration(
144 | color: ConfigColor.colorContentBackground,
145 | boxShadow: [
146 | new BoxShadow(color: Color(0x18000000), blurRadius: 3.0)
147 | ]),
148 | height: 48.0,
149 | child: Row(children: [
150 | getBottomIcon(0),
151 | getBottomIcon(1),
152 | getBottomIcon(2),
153 | getBottomIcon(3),
154 | getBottomIcon(4),
155 | ]),
156 | ),
157 | ],
158 | ),
159 | );
160 | }
161 | }
162 |
--------------------------------------------------------------------------------
/lib/page/picturePage.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class PicturePage extends StatefulWidget {
4 | final String url;
5 |
6 | PicturePage({Key key, @required this.url}) : super(key: key);
7 |
8 | @override
9 | State createState() {
10 | return _PictureState(url);
11 | }
12 | }
13 |
14 | class _PictureState extends State {
15 | String url;
16 |
17 | _PictureState(this.url);
18 |
19 | @override
20 | Widget build(BuildContext context) {
21 | return Container(
22 | color: Colors.white,
23 | child: Hero(
24 | tag: "img",
25 | child: Image.network(
26 | url,
27 | fit: BoxFit.contain,
28 | )),
29 | );
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/lib/page/pinPage.dart:
--------------------------------------------------------------------------------
1 | import 'dart:convert';
2 |
3 | import 'package:flutter/material.dart';
4 | import 'package:http/http.dart' as http;
5 | import 'package:juejin_flutter/config/config_color.dart';
6 | import 'package:juejin_flutter/model/pin.dart';
7 | import 'package:juejin_flutter/page/picturePage.dart';
8 | import 'package:juejin_flutter/widget/banner/banner_evalutor.dart';
9 |
10 | class PinPage extends StatefulWidget {
11 | @override
12 | State createState() {
13 | return _PinPageState();
14 | }
15 | }
16 |
17 | class _PinPageState extends State with AutomaticKeepAliveClientMixin {
18 | List items = List();
19 | ScrollController _scrollController = ScrollController();
20 | var before = "";
21 |
22 | Future> fetchPost() async {
23 | final response = await http.get(
24 | 'https://short-msg-ms.juejin.im/v1/pinList/recommend?uid=&before=${before}&limit=20&device_id=asdasd&token=&src=android');
25 |
26 | if (response.statusCode == 200) {
27 | // If the call to the server was successful, parse the JSON
28 | var decode = json.decode(response.body);
29 | var entrylist = decode['d']['list'];
30 | var list = List();
31 | for (var value in entrylist) {
32 | try {
33 | var bean = Pin.fromJson(value);
34 | list.add(bean);
35 | } catch (e) {
36 | print(e);
37 | }
38 | }
39 | before = list.last.createdAt;
40 | return list;
41 | } else {
42 | // If that call was not successful, throw an error.
43 | throw Exception('Failed to load post');
44 | }
45 | }
46 |
47 | @override
48 | void initState() {
49 | _onRefresh();
50 | _scrollController.addListener(() {
51 | if (_scrollController.position.pixels ==
52 | _scrollController.position.maxScrollExtent) {
53 | print("loadMore");
54 | _getMoreData();
55 | }
56 | });
57 | super.initState();
58 | }
59 |
60 | var tabs = ['话题', '推荐', '关注'];
61 |
62 | Widget getTabItemWidget(int index) {
63 | return Container(
64 | height: kToolbarHeight,
65 | child: Center(
66 | child: Text(
67 | tabs[index],
68 | textAlign: TextAlign.center,
69 | ),
70 | ),
71 | );
72 | }
73 |
74 | var width = 360.0;
75 |
76 | @override
77 | Widget build(BuildContext context) {
78 | width = MediaQuery.of(context).size.width;
79 | var statusBarHeight = MediaQuery.of(context).padding.top;
80 | var tabHeight = 40.0 + statusBarHeight;
81 | return MaterialApp(
82 | home: DefaultTabController(
83 | length: tabs.length,
84 | child: Scaffold(
85 | body: Stack(
86 | children: [
87 | Container(
88 | margin: EdgeInsets.only(top: tabHeight - 22.0),
89 | child: RefreshIndicator(
90 | onRefresh: _onRefresh,
91 | child: ListView.builder(
92 | itemCount: items.length <= 0 ? 0 : items.length + 2,
93 | itemBuilder: (context, index) {
94 | if (index == 0) {
95 | return _buildExploreHeader();
96 | } else if (index == items.length + 1) {
97 | return _buildProgressIndicator();
98 | } else {
99 | var i = index - 1;
100 | if (i >= 0 && i < items.length) {
101 | final item = items[i];
102 | return getItemView(item);
103 | } else {
104 | return _buildProgressIndicator();
105 | }
106 | }
107 | },
108 | controller: _scrollController,
109 | ),
110 | ),
111 | ),
112 | Container(
113 | decoration: BoxDecoration(
114 | color: ConfigColor.colorPrimary,
115 | boxShadow: [
116 | BoxShadow(
117 | color: Color(0x44000000),
118 | blurRadius: 3.0,
119 | offset: Offset(0.0, 1.0))
120 | ]),
121 | padding: EdgeInsets.only(top: statusBarHeight),
122 | height: tabHeight,
123 | width: double.infinity,
124 | child: Center(
125 | child: TabBar(
126 | isScrollable: true,
127 | indicatorWeight: 2.5,
128 | indicatorSize: TabBarIndicatorSize.label,
129 | indicatorColor: ConfigColor.colorContentBackground,
130 | tabs: [
131 | getTabItemWidget(0),
132 | getTabItemWidget(1),
133 | getTabItemWidget(2),
134 | ],
135 | ),
136 | )),
137 | ],
138 | )),
139 | ));
140 | }
141 |
142 | Widget _buildProgressIndicator() {
143 | return Container(
144 | margin: EdgeInsets.all(16.0),
145 | child: Center(
146 | child: SizedBox(
147 | child: CircularProgressIndicator(
148 | strokeWidth: 2.0,
149 | ),
150 | height: 20.0,
151 | width: 20.0,
152 | ),
153 | ));
154 | }
155 |
156 | Widget _buildExploreHeader() {
157 | return Container();
158 | }
159 |
160 | @override
161 | void dispose() {
162 | super.dispose();
163 | _scrollController.dispose();
164 | }
165 |
166 | Widget getItemView(Pin pin) {
167 | return Container(
168 | padding:
169 | EdgeInsets.only(top: 8.0, right: 16.0, bottom: 8.0, left: 16.0),
170 | margin: EdgeInsets.only(top: 8.0),
171 | decoration: BoxDecoration(
172 | color: ConfigColor.colorContentBackground,
173 | boxShadow: [
174 | new BoxShadow(
175 | color: Color(0x18000000),
176 | blurRadius: 1.0,
177 | offset: Offset(0.0, 0.5))
178 | ]),
179 | child: Column(
180 | crossAxisAlignment: CrossAxisAlignment.start,
181 | children: [
182 | Container(
183 | height: 38.0,
184 | child: Row(
185 | children: [
186 | Container(
187 | margin: EdgeInsets.only(left: 0.0),
188 | child: CircleAvatar(
189 | backgroundImage: NetworkImage(pin.user.avatarLarge),
190 | backgroundColor: ConfigColor.colorWindowBackground,
191 | ),
192 | padding: const EdgeInsets.all(1.0),
193 | // borde width
194 | decoration: new BoxDecoration(
195 | color: Color(0xffd7dade), // border color
196 | shape: BoxShape.circle,
197 | ),
198 | width: 24.0,
199 | height: 24.0),
200 | Expanded(
201 | child: Container(
202 | margin: EdgeInsets.only(left: 8.0),
203 | child: Text(
204 | pin.user.username,
205 | maxLines: 1,
206 | style: TextStyle(
207 | fontSize: 13.0, color: ConfigColor.colorText2),
208 | ),
209 | ),
210 | ),
211 | Container(
212 | margin: EdgeInsets.only(left: 8.0),
213 | child: Text(
214 | "3小时前",
215 | maxLines: 1,
216 | style: TextStyle(
217 | fontSize: 10.0, color: ConfigColor.colorText3),
218 | ),
219 | ),
220 | ],
221 | ),
222 | ),
223 | Container(
224 | margin: EdgeInsets.only(top: 0.0, bottom: 8.0),
225 | child: Text(
226 | pin.content,
227 | maxLines: 5,
228 | overflow: TextOverflow.ellipsis,
229 | softWrap: true,
230 | style: TextStyle(
231 | fontSize: 14.0, color: ConfigColor.colorText1, height: 1.2),
232 | ),
233 | width: double.infinity,
234 | ),
235 | getPicturesWidget(pin),
236 | getUrlWidget(pin),
237 | getTopicWidget(pin),
238 | getBottomWidget(pin),
239 | ],
240 | ));
241 | }
242 |
243 | Widget getBottomWidget(Pin pin) {
244 | var likeText = pin.likedCount <= 0 ? "赞" : "${pin.likedCount}";
245 | var commentText = pin.commentCount <= 0 ? "评论" : "${pin.commentCount}";
246 | return Container(
247 | height: 40.0,
248 | child: Row(
249 | children: [
250 | Container(
251 | width: 64.0,
252 | child: Row(
253 | children: [
254 | Icon(
255 | Icons.thumb_up,
256 | size: 16.0,
257 | color: Colors.black26,
258 | ),
259 | Container(
260 | margin: EdgeInsets.only(left: 4.0),
261 | child: Text(likeText,
262 | style: TextStyle(
263 | fontSize: 12.0, color: ConfigColor.colorText3)),
264 | ),
265 | ],
266 | ),
267 | ),
268 | Container(
269 | width: 64.0,
270 | child: Row(
271 | children: [
272 | Icon(
273 | Icons.comment,
274 | size: 16.0,
275 | color: Colors.black26,
276 | ),
277 | Container(
278 | margin: EdgeInsets.only(left: 4.0),
279 | child: Text(commentText,
280 | style: TextStyle(
281 | fontSize: 12.0, color: ConfigColor.colorText3)),
282 | ),
283 | ],
284 | ),
285 | ),
286 | Container(
287 | width: 64.0,
288 | child: Row(
289 | children: [
290 | Icon(
291 | Icons.share,
292 | size: 16.0,
293 | color: Colors.black26,
294 | ),
295 | Container(
296 | margin: EdgeInsets.only(left: 4.0),
297 | child: Text("分享",
298 | style: TextStyle(
299 | fontSize: 12.0, color: ConfigColor.colorText3)),
300 | ),
301 | ],
302 | ),
303 | )
304 | ],
305 | ),
306 | );
307 | }
308 |
309 | Future _onRefresh() async {
310 | before = "";
311 | await fetchPost().then((list) {
312 | setState(() {
313 | items.clear();
314 | items.addAll(list);
315 | return null;
316 | });
317 | });
318 | }
319 |
320 | Future _getMoreData() async {
321 | await fetchPost().then((list) {
322 | setState(() {
323 | items.addAll(list);
324 | return null;
325 | });
326 | });
327 | }
328 |
329 | Widget getImgInGridView(
330 | String url, double size, bool marginLeft, bool marginTop) {
331 | return Container(
332 | decoration: BoxDecoration(
333 | border: Border.all(color: ConfigColor.colorDivider),
334 | borderRadius: BorderRadius.all(Radius.circular(3.0)),
335 | ),
336 | margin: EdgeInsets.only(
337 | top: marginTop ? 4.0 : 0.0, left: marginLeft ? 4.0 : 0.0),
338 | height: size,
339 | width: size,
340 | child: ClipRRect(
341 | borderRadius: new BorderRadius.circular(3.0),
342 | child: getNetworkImage(url),
343 | ),
344 | );
345 | }
346 |
347 | Widget getNetworkImage(String url) {
348 | try {
349 | return InkWell(
350 | child: Hero(
351 | tag: "img",
352 | child: FadeInImage.assetNetwork(
353 | placeholder: "assets/loading.png",
354 | image: url,
355 | fit: BoxFit.cover,
356 | )),
357 | onTap: () {
358 | Navigator.push(context,
359 | MaterialPageRoute(builder: (context) => PicturePage(url: url)));
360 | },
361 | );
362 | } catch (e) {
363 | print(e);
364 | return Image.asset("assets/loading.png");
365 | }
366 | }
367 |
368 | Widget getPicturesWidget(Pin pin) {
369 | if (pin.pictures == null || pin.pictures.length <= 0) return Container();
370 | var pictures = pin.pictures;
371 | if (pictures.length == 1) {
372 | var url = pictures[0];
373 | var w = 200;
374 | var h = 200;
375 | try {
376 | var wStr = Uri.parse(url).queryParameters['w'];
377 | var hStr = Uri.parse(url).queryParameters['h'];
378 | w = int.parse(wStr);
379 | h = int.parse(hStr);
380 | } catch (e) {}
381 |
382 | var imgW = 200.0;
383 | var imgH = 200.0;
384 | if (w >= h) {
385 | imgW = 200.0;
386 | imgH = h / w * imgW;
387 | } else {
388 | imgH = 200.0;
389 | imgW = w * imgH / h;
390 | }
391 | if (imgH == 200.0 && imgW <= 140.0) {
392 | imgW = 140.0;
393 | }
394 |
395 | return Container(
396 | margin: EdgeInsets.only(top: 8.0, bottom: 16.0),
397 | decoration: BoxDecoration(
398 | border: Border.all(color: ConfigColor.colorDivider),
399 | borderRadius: BorderRadius.all(Radius.circular(3.0)),
400 | ),
401 | child: ClipRRect(
402 | borderRadius: new BorderRadius.circular(3.0),
403 | child: getNetworkImage(pictures[0])),
404 | width: imgW,
405 | height: imgH,
406 | );
407 | }
408 | if (pictures.length == 2) {
409 | double size = (width - 32 - 4) / 2.0;
410 | return Row(
411 | children: [
412 | getImgInGridView(pictures[0], size, false, false),
413 | getImgInGridView(pictures[1], size, true, false),
414 | ],
415 | );
416 | } else if (pictures.length == 3) {
417 | double size = (width - 32 - 8) / 3.0;
418 | return Row(
419 | children: [
420 | getImgInGridView(pictures[0], size, false, false),
421 | getImgInGridView(pictures[1], size, true, false),
422 | getImgInGridView(pictures[2], size, true, false),
423 | ],
424 | );
425 | } else if (pictures.length == 4) {
426 | double size = (width - 32 - 4) / 2.0;
427 | return Column(
428 | children: [
429 | Row(
430 | children: [
431 | getImgInGridView(pictures[0], size, false, false),
432 | getImgInGridView(pictures[1], size, true, false),
433 | ],
434 | ),
435 | Row(
436 | children: [
437 | getImgInGridView(pictures[2], size, false, true),
438 | getImgInGridView(pictures[3], size, true, true),
439 | ],
440 | )
441 | ],
442 | );
443 | } else if (pictures.length == 5) {
444 | double size = (width - 32 - 4) / 2.0;
445 | double size1 = (width - 32 - 8) / 3.0;
446 | return Column(
447 | children: [
448 | Row(
449 | children: [
450 | getImgInGridView(pictures[0], size, false, false),
451 | getImgInGridView(pictures[1], size, true, false),
452 | ],
453 | ),
454 | Row(
455 | children: [
456 | getImgInGridView(pictures[2], size1, false, true),
457 | getImgInGridView(pictures[3], size1, true, true),
458 | getImgInGridView(pictures[4], size1, true, true),
459 | ],
460 | )
461 | ],
462 | );
463 | } else if (pictures.length == 6) {
464 | double size = (width - 32 - 8) / 3.0;
465 | return Column(
466 | children: [
467 | Row(
468 | children: [
469 | getImgInGridView(pictures[0], size, false, false),
470 | getImgInGridView(pictures[1], size, true, false),
471 | getImgInGridView(pictures[2], size, true, false),
472 | ],
473 | ),
474 | Row(
475 | children: [
476 | getImgInGridView(pictures[3], size, false, true),
477 | getImgInGridView(pictures[4], size, true, true),
478 | getImgInGridView(pictures[5], size, true, true),
479 | ],
480 | )
481 | ],
482 | );
483 | } else if (pictures.length == 7) {
484 | double size = (width - 32 - 8) / 3.0;
485 | return Column(
486 | children: [
487 | Row(
488 | children: [
489 | getImgInGridView(pictures[0], width - 32.0, false, false),
490 | ],
491 | ),
492 | Row(
493 | children: [
494 | getImgInGridView(pictures[1], size, false, true),
495 | getImgInGridView(pictures[2], size, true, true),
496 | getImgInGridView(pictures[3], size, true, true),
497 | ],
498 | ),
499 | Row(
500 | children: [
501 | getImgInGridView(pictures[4], size, false, true),
502 | getImgInGridView(pictures[5], size, true, true),
503 | getImgInGridView(pictures[6], size, true, true),
504 | ],
505 | )
506 | ],
507 | );
508 | } else if (pictures.length == 8) {
509 | double size = (width - 32 - 8) / 3.0;
510 | double size1 = (width - 32 - 4) / 2.0;
511 | return Column(
512 | children: [
513 | Row(
514 | children: [
515 | getImgInGridView(pictures[0], size1, false, false),
516 | getImgInGridView(pictures[1], size1, true, false),
517 | ],
518 | ),
519 | Row(
520 | children: [
521 | getImgInGridView(pictures[2], size, false, true),
522 | getImgInGridView(pictures[3], size, true, true),
523 | getImgInGridView(pictures[4], size, true, true),
524 | ],
525 | ),
526 | Row(
527 | children: [
528 | getImgInGridView(pictures[5], size, false, true),
529 | getImgInGridView(pictures[6], size, true, true),
530 | getImgInGridView(pictures[7], size, true, true),
531 | ],
532 | )
533 | ],
534 | );
535 | } else if (pictures.length == 9) {
536 | double size = (width - 32 - 8) / 3.0;
537 | return Column(
538 | children: [
539 | Row(
540 | children: [
541 | getImgInGridView(pictures[0], size, false, false),
542 | getImgInGridView(pictures[1], size, true, false),
543 | getImgInGridView(pictures[2], size, true, false),
544 | ],
545 | ),
546 | Row(
547 | children: [
548 | getImgInGridView(pictures[3], size, false, true),
549 | getImgInGridView(pictures[4], size, true, true),
550 | getImgInGridView(pictures[5], size, true, true),
551 | ],
552 | ),
553 | Row(
554 | children: [
555 | getImgInGridView(pictures[6], size, false, true),
556 | getImgInGridView(pictures[7], size, true, true),
557 | getImgInGridView(pictures[8], size, true, true),
558 | ],
559 | )
560 | ],
561 | );
562 | }
563 | var imageRow = List();
564 | for (var value in pictures) {
565 | imageRow.add(Expanded(
566 | child: Container(
567 | margin: EdgeInsets.only(top: 16.0, bottom: 16.0),
568 | height: 80.0,
569 | child: Image.network(
570 | value,
571 | fit: BoxFit.cover,
572 | ),
573 | ),
574 | ));
575 | }
576 | return Row(
577 | children: imageRow,
578 | );
579 | }
580 |
581 | Widget getTopicWidget(Pin pin) {
582 | if (pin.topic == null) return Container();
583 | return Container(
584 | padding: EdgeInsets.only(left: 8.0, right: 8.0, top: 4.0, bottom: 4.0),
585 | margin: EdgeInsets.only(right: 16.0, bottom: 8.0, top: 8.0),
586 | decoration: BoxDecoration(
587 | color: Color(0xffF6F8FA),
588 | borderRadius: BorderRadius.all(Radius.circular(4.0))),
589 | child: Text(
590 | "# ${pin.topic.title}",
591 | textAlign: TextAlign.center,
592 | style: TextStyle(fontSize: 11.0, color: ConfigColor.colorPrimary),
593 | ),
594 | );
595 | }
596 |
597 | Widget getUrlWidget(Pin pin) {
598 | if (pin.url == null || pin.url.isEmpty) return Container();
599 | print(pin);
600 | var uri = Uri.parse(pin.url);
601 | var urlPic = pin.urlPic != null && pin.urlPic.isNotEmpty ? pin.urlPic : "";
602 | var urlTitle =
603 | pin.urlTitle != null && pin.urlTitle.isNotEmpty ? pin.urlTitle : "网页链接";
604 | return Container(
605 | height: 80.0,
606 | padding: EdgeInsets.only(left: 12.0, right: 8.0, top: 8.0, bottom: 8.0),
607 | margin: EdgeInsets.only(right: 16.0, bottom: 8.0, top: 8.0),
608 | decoration: BoxDecoration(
609 | color: Color(0xffF6F8FA),
610 | borderRadius: BorderRadius.all(Radius.circular(4.0))),
611 | child: Row(
612 | crossAxisAlignment: CrossAxisAlignment.stretch,
613 | children: [
614 | Expanded(
615 | child: Column(
616 | crossAxisAlignment: CrossAxisAlignment.start,
617 | mainAxisAlignment: MainAxisAlignment.spaceBetween,
618 | children: [
619 | Text(
620 | urlTitle,
621 | style:
622 | TextStyle(color: ConfigColor.colorText1, fontSize: 14.0),
623 | ),
624 | Container(
625 | margin: EdgeInsets.only(bottom: 4.0),
626 | child: Text(
627 | uri.host,
628 | style: TextStyle(
629 | color: ConfigColor.colorText3, fontSize: 12.0),
630 | ),
631 | ),
632 | ],
633 | ),
634 | ),
635 | Container(
636 | height: 64.0,
637 | width: 64.0,
638 | child: ClipRRect(
639 | borderRadius: BorderRadius.circular(3.0),
640 | child: Image.network(
641 | urlPic,
642 | fit: BoxFit.cover,
643 | ),
644 | ),
645 | ),
646 | ],
647 | ),
648 | );
649 | }
650 |
651 | @override
652 | bool get wantKeepAlive => true;
653 | }
654 |
655 | class Model extends Object with BannerWithEval {
656 | final String imgUrl;
657 |
658 | Model({this.imgUrl});
659 |
660 | @override
661 | get bannerUrl => imgUrl;
662 | }
663 |
--------------------------------------------------------------------------------
/lib/page/userPage.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:juejin_flutter/config/config_color.dart';
3 |
4 | class UserPage extends StatefulWidget {
5 | @override
6 | State createState() {
7 | return _UserPage();
8 | }
9 | }
10 |
11 | class _UserPage extends State with AutomaticKeepAliveClientMixin {
12 | @override
13 | bool get wantKeepAlive => true;
14 |
15 | @override
16 | Widget build(BuildContext context) {
17 | return MaterialApp(
18 | title: "user",
19 | home: Scaffold(
20 | appBar: AppBar(
21 | title: Text("我"),
22 | backgroundColor: ConfigColor.colorPrimary,
23 | elevation: 1.0,
24 | ),
25 | body: SingleChildScrollView(
26 | child: Column(children: [
27 | Container(
28 | height: 80.0,
29 | margin: EdgeInsets.only(top: 12.0),
30 | padding: EdgeInsets.only(
31 | left: 16.0, right: 16.0, top: 16.0, bottom: 16.0),
32 | decoration: BoxDecoration(
33 | color: ConfigColor.colorContentBackground,
34 | boxShadow: [
35 | BoxShadow(
36 | color: Color(0x18000000),
37 | blurRadius: 1.0,
38 | offset: Offset(0.0, 0.5)),
39 | ]),
40 | child: Row(children: [
41 | Container(
42 | height: 48.0,
43 | width: 48.0,
44 | child: CircleAvatar(
45 | backgroundImage: NetworkImage(
46 | "https://user-gold-cdn.xitu.io/2018/7/6/1646db21990a14f9"),
47 | backgroundColor: ConfigColor.colorWindowBackground),
48 | ),
49 | Expanded(
50 | child: Column(
51 | crossAxisAlignment: CrossAxisAlignment.start,
52 | children: [
53 | Container(
54 | padding: const EdgeInsets.only(left: 8.0, top: 4.0),
55 | child: Text(
56 | "码农码畜",
57 | textAlign: TextAlign.left,
58 | style: TextStyle(
59 | fontWeight: FontWeight.bold,
60 | fontSize: 16.0,
61 | color: ConfigColor.colorText1),
62 | ),
63 | ),
64 | Container(
65 | padding: const EdgeInsets.only(left: 8.0, top: 2.0),
66 | child: Text(
67 | "宇航员 @ 航天局",
68 | style: TextStyle(
69 | fontSize: 12.0, color: ConfigColor.colorText2),
70 | ),
71 | ),
72 | ],
73 | ),
74 | ),
75 | Icon(
76 | Icons.keyboard_arrow_right,
77 | color: ConfigColor.colorDivider,
78 | ),
79 | ]),
80 | ),
81 |
82 | // items
83 | Container(
84 | margin: EdgeInsets.only(top: 12.0),
85 | decoration: BoxDecoration(
86 | color: ConfigColor.colorContentBackground,
87 | boxShadow: [
88 | BoxShadow(
89 | color: Color(0x18000000),
90 | blurRadius: 1.0,
91 | offset: Offset(0.0, 0.5)),
92 | ]),
93 | child: Column(
94 | children: [
95 | getSettingItem(
96 | Icons.notifications, Color(0xFF0076FF), "消息中心", ""),
97 | getSettingItem(
98 | Icons.favorite, Color(0xFF6CBD45), "我赞过的", ""),
99 | getSettingItem(
100 | Icons.grade, Color(0xFFffc347), "收藏集合", "50个"),
101 | getSettingItem(Icons.shopping_basket, Color(0xFFffbe4b),
102 | "已购小册", "10本"),
103 | getSettingItem(Icons.remove_red_eye, Color(0xFFABB4BF),
104 | "阅读历史", "7001篇"),
105 | getSettingItem(
106 | Icons.loyalty, Color(0xFFABB4BF), "标签管理", "14个"),
107 | ],
108 | )),
109 |
110 | // items
111 | Container(
112 | margin: EdgeInsets.only(top: 12.0, bottom: 12.0),
113 | decoration: BoxDecoration(
114 | color: ConfigColor.colorContentBackground,
115 | boxShadow: [
116 | BoxShadow(
117 | color: Color(0x18000000),
118 | blurRadius: 1.0,
119 | offset: Offset(0.0, 0.5)),
120 | ]),
121 | child: Column(
122 | children: [
123 | getSettingItem(
124 | Icons.brightness_medium, Color(0xFFABB4BF), "夜间模式", ""),
125 | getSettingItem(
126 | Icons.feedback, Color(0xFFABB4BF), "意见反馈", ""),
127 | getSettingItem(
128 | Icons.settings, Color(0xFFABB4BF), "应用设置", ""),
129 | ],
130 | )),
131 | ]))),
132 | );
133 | }
134 |
135 | Widget getSettingItem(IconData icon, Color color, String title, String text) {
136 | return Column(children: [
137 | Container(
138 | height: 48.0,
139 | padding: const EdgeInsets.only(left: 16.0, right: 16.0),
140 | child: Row(
141 | children: [
142 | Icon(
143 | icon,
144 | color: color,
145 | size: 20.0,
146 | ),
147 | Expanded(
148 | child: Container(
149 | padding: const EdgeInsets.only(left: 16.0),
150 | child: Text(
151 | title,
152 | style: TextStyle(
153 | color: ConfigColor.colorText1,
154 | fontSize: 15.0,
155 | ),
156 | ),
157 | ),
158 | ),
159 | Text(
160 | text,
161 | style: TextStyle(fontSize: 12.0, color: ConfigColor.colorText2),
162 | ),
163 | ],
164 | ),
165 | ),
166 | Container(
167 | height: 0.5,
168 | color: ConfigColor.colorDivider,
169 | )
170 | ]);
171 | }
172 | }
173 |
--------------------------------------------------------------------------------
/lib/page/webpage.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_webview_plugin/flutter_webview_plugin.dart';
3 | import 'package:juejin_flutter/config/config_color.dart';
4 |
5 | class WebPage extends StatefulWidget {
6 | final String url;
7 |
8 | WebPage({Key key, @required this.url}) : super(key: key);
9 |
10 | @override
11 | State createState() {
12 | return _WebPageState(url);
13 | }
14 | }
15 |
16 | class _WebPageState extends State {
17 | String url;
18 |
19 | _WebPageState(this.url);
20 |
21 | @override
22 | Widget build(BuildContext context) {
23 | return Scaffold(
24 | body: WebviewScaffold(
25 | url: url,
26 | appBar: new AppBar(
27 | iconTheme: IconThemeData(color: ConfigColor.colorText1),
28 | backgroundColor: ConfigColor.colorContentBackground,
29 | elevation: 2.0,
30 | title: new Text(
31 | "文章详情页",
32 | style: TextStyle(color: ConfigColor.colorText1),
33 | ),
34 | actions: [
35 | Container(
36 | child: Icon(
37 | Icons.more_horiz,
38 | ),
39 | margin: EdgeInsets.all(8.0),
40 | )
41 | ],
42 | ),
43 | ),
44 | );
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/lib/page/xiaocePage.dart:
--------------------------------------------------------------------------------
1 | import 'dart:convert';
2 |
3 | import 'package:flutter/material.dart';
4 | import 'package:http/http.dart' as http;
5 | import 'package:juejin_flutter/config/config_color.dart';
6 | import 'package:juejin_flutter/model/entry.dart';
7 | import 'package:juejin_flutter/model/xiaoce.dart';
8 |
9 | Future> fetchPost() async {
10 | final response = await http.get(
11 | 'https://xiaoce-timeline-api-ms.juejin.im/v1/getListByLastTime?&src=web&alias=&pageNum=1');
12 |
13 | if (response.statusCode == 200) {
14 | // If the call to the server was successful, parse the JSON
15 | var decode = json.decode(response.body);
16 | var xiaoceList = decode['d'];
17 | var list = List();
18 | for (var value in xiaoceList) {
19 | try {
20 | var entry = Xiaoce.fromJson(value);
21 | list.add(entry);
22 | } catch (e) {
23 | print(e);
24 | }
25 | }
26 | return list;
27 | } else {
28 | // If that call was not successful, throw an error.
29 | throw Exception('Failed to load post');
30 | }
31 | }
32 |
33 | class XiaocePage extends StatefulWidget {
34 | @override
35 | State createState() {
36 | return _XiaocePageState();
37 | }
38 | }
39 |
40 | class _XiaocePageState extends State with AutomaticKeepAliveClientMixin {
41 |
42 | @override
43 | bool get wantKeepAlive => true;
44 |
45 | List items = List();
46 | ScrollController _scrollController = ScrollController();
47 |
48 | @override
49 | void initState() {
50 | _onRefresh();
51 | _scrollController.addListener(() {
52 | if (_scrollController.position.pixels ==
53 | _scrollController.position.maxScrollExtent) {
54 | print("loadMore");
55 | _getMoreData();
56 | }
57 | });
58 | super.initState();
59 | }
60 |
61 | @override
62 | Widget build(BuildContext context) {
63 | return MaterialApp(
64 | title: "小册",
65 | home: Scaffold(
66 | appBar: AppBar(
67 | title: Text("小册"),
68 | backgroundColor: ConfigColor.colorPrimary,
69 | elevation: 1.0,
70 | ),
71 | body: RefreshIndicator(
72 | onRefresh: _onRefresh,
73 | child: ListView.builder(
74 | itemCount: items.length <= 0 ? 0 : items.length + 1,
75 | itemBuilder: (context, index) {
76 | if (index == items.length) {
77 | return _buildProgressIndicator();
78 | } else {
79 | final item = items[index];
80 | return getItemView(item);
81 | }
82 | },
83 | controller: _scrollController,
84 | ),
85 | ),
86 | ),
87 | );
88 | }
89 |
90 | Widget _buildProgressIndicator() {
91 | return Container(
92 | margin: EdgeInsets.all(16.0),
93 | child: Center(
94 | child: SizedBox(
95 | child: CircularProgressIndicator(
96 | strokeWidth: 2.0,
97 | ),
98 | height: 20.0,
99 | width: 20.0,
100 | ),
101 | ));
102 | }
103 |
104 | @override
105 | void dispose() {
106 | super.dispose();
107 | _scrollController.dispose();
108 | }
109 |
110 | Widget getItemView(Xiaoce xiaoce) {
111 | return Container(
112 | decoration: BoxDecoration(
113 | color: ConfigColor.colorContentBackground,
114 | border: BorderDirectional(
115 | bottom: BorderSide(color: ConfigColor.colorDivider, width: 0.5))),
116 | padding: EdgeInsets.only(top: 16.0),
117 | child: Row(
118 | children: [
119 | Container(
120 | decoration: BoxDecoration(
121 | color: ConfigColor.colorWindowBackground,
122 | boxShadow: [
123 | new BoxShadow(color: Color(0x33000000), blurRadius: 4.0, offset: Offset(1.0, 1.0))
124 | ]
125 | ),
126 | margin: EdgeInsets.only(left: 16.0, bottom: 16.0),
127 | child: Image.network(xiaoce.img,
128 | fit: BoxFit.cover, width: 72.0, height: 100.0),
129 | ),
130 | Expanded(
131 | child: Container(
132 | height: 100.0,
133 | margin: EdgeInsets.only(left: 12.0, bottom: 16.0),
134 | child: Column(
135 | mainAxisAlignment: MainAxisAlignment.spaceAround,
136 | crossAxisAlignment: CrossAxisAlignment.start,
137 | children: [
138 | Container(
139 | child: Text(
140 | xiaoce.title,
141 | maxLines: 2,
142 | style: TextStyle(
143 | fontWeight: FontWeight.bold,
144 | fontSize: 16.0, color: ConfigColor.colorText1),
145 | ),
146 | ),
147 | Container(
148 | child: Text(
149 | xiaoce.userData.username,
150 | style: TextStyle(
151 | fontSize: 14.0, color: ConfigColor.colorText2),
152 | ),
153 | ),
154 | Container(
155 | child: Text(
156 | "${xiaoce.buyCount} 人购买",
157 | style: TextStyle(
158 | fontSize: 12.0, color: ConfigColor.colorText3),
159 | ),
160 | ),
161 | ],
162 | ),
163 | ),
164 | ),
165 | Container(
166 | height: 28.0,
167 | width: 72.0,
168 | margin: EdgeInsets.only(right: 16.0, left: 16.0, bottom: 16.0),
169 | decoration: BoxDecoration(
170 | color: Color(0xFFf0f7ff),
171 | borderRadius: BorderRadius.all(Radius.circular(20.0))),
172 | child: Center(child: Text(
173 | "¥${xiaoce.price}",
174 | textAlign: TextAlign.center,
175 | style: TextStyle( fontWeight: FontWeight.bold, fontSize: 15.0, color: ConfigColor.colorPrimary),
176 | ),),
177 | )
178 | ],
179 | ),
180 | );
181 | }
182 |
183 | Future _onRefresh() async {
184 | await fetchPost().then((list) {
185 | setState(() {
186 | items.clear();
187 | items.addAll(list);
188 | return null;
189 | });
190 | });
191 | }
192 |
193 | Future _getMoreData() async {
194 | await fetchPost().then((list) {
195 | setState(() {
196 | items.addAll(list);
197 | return null;
198 | });
199 | });
200 | }
201 | }
202 |
--------------------------------------------------------------------------------
/lib/widget/banner/banner_evalutor.dart:
--------------------------------------------------------------------------------
1 | abstract class BannerWithEval{
2 | get bannerUrl;
3 | }
--------------------------------------------------------------------------------
/lib/widget/banner/banner_widget.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import './banner_evalutor.dart';
3 | import 'dart:async';
4 |
5 | class BannerWidget extends StatefulWidget{
6 | final List data;
7 | final int height,delayTime,duration;
8 | final Curve curve;
9 | final ItemBuild build;
10 | final IndicatorBuild indicator;
11 | final OnClick onClick;
12 |
13 | BannerWidget({
14 | Key key,
15 | @required this.data,
16 | @required this.curve,
17 | this.indicator,
18 | this.build,
19 | this.onClick,
20 | this.height = 160,
21 | this.delayTime = 4000,
22 | this.duration = 1000,
23 | }): super(key : key);
24 |
25 | createState() => BannerState();
26 | }
27 |
28 | class BannerState extends State {
29 | Timer timer;
30 | PageController controller;
31 | int position,currentPage;
32 | List bannerList = [];
33 | bool isRoll = true;
34 |
35 | @override
36 | void initState() {
37 | position = 0;
38 | currentPage = -1;
39 | controller = PageController(initialPage: getRealCount());
40 | restTime();
41 | super.initState();
42 | }
43 |
44 | @override
45 | Widget build(BuildContext context) {
46 | restData();
47 | return Container(
48 | height: widget.height.toDouble(),
49 | color: Colors.grey,
50 | child: Stack(
51 | children: [
52 | pageView(),
53 | indicator(),
54 | ],
55 | ));
56 | }
57 |
58 | Widget pageView() {
59 | return Listener(
60 | onPointerMove: (event){
61 | isRoll = true;
62 | },
63 | onPointerDown: (event){
64 | isRoll = false;
65 | },
66 | onPointerUp: (event){
67 | isRoll = true;
68 | },
69 | onPointerCancel: (event){
70 | isRoll = true;
71 | },
72 | child: NotificationListener(
73 | onNotification: (scrollNotification){
74 | if (currentPage == -1) {
75 | isRoll = true;
76 | }else if (scrollNotification is ScrollEndNotification || scrollNotification is UserScrollNotification) {
77 | if (currentPage == 0) {
78 | setState(() {
79 | currentPage = getRealCount();
80 | controller.jumpToPage(currentPage);
81 | });
82 | }
83 | isRoll = true;
84 | } else {
85 | isRoll = false;
86 | }
87 | },
88 | child: PageView.custom(
89 | controller: controller,
90 | onPageChanged: (index) {
91 | currentPage = index;
92 | position = index % getRealCount();
93 | setState(() {});
94 | },
95 | physics: const PageScrollPhysics(parent: const BouncingScrollPhysics()),
96 | childrenDelegate: SliverChildBuilderDelegate((context, index){
97 | int current = index % getRealCount();
98 | BannerWithEval bannerWithEval = bannerList[current];
99 | return GestureDetector(
100 | onTap: () => widget.onClick(current, bannerWithEval),
101 | child: widget.build != null ? widget.build(current) : BannerItem(
102 | url: bannerWithEval.bannerUrl,
103 | ),
104 | );
105 | },
106 | childCount: bannerList.length,
107 | ),
108 | ),
109 | ),
110 | );
111 | }
112 |
113 | Widget indicator() {
114 | return widget.indicator == null ? Align(
115 | alignment: Alignment.bottomCenter,
116 | child: Container(
117 | height: 20.0,
118 | padding: EdgeInsets.all(2.0),
119 | child: Row(
120 | mainAxisAlignment: MainAxisAlignment.center,
121 | children: circularPoint(getRealCount()),
122 | ),
123 | )) : widget.indicator(position,getRealCount());
124 | }
125 |
126 | List circularPoint(int count) {
127 | List children = [];
128 | for (var i = 0; i < count; i++) {
129 | children.add(Container(
130 | width: position == i ? 16.0: 8.0,
131 | height: 2.0,
132 | margin: EdgeInsets.only(left: 4.0,top: 0.0,right: 4.0,bottom: 0.0),
133 | decoration: BoxDecoration(
134 | shape: BoxShape.rectangle,
135 | color: position == i ? Colors.white : Colors.white54,
136 | ),
137 | ));
138 | }
139 | return children;
140 | }
141 |
142 | void restTime() {
143 | if (timer == null){
144 | timer = Timer.periodic(Duration(milliseconds: widget.delayTime), (timer) {
145 | if (isRoll){
146 | if (currentPage == -1){
147 | currentPage = 0;
148 | controller.jumpToPage(currentPage);
149 | return;
150 | }
151 | currentPage++;
152 | controller.nextPage(duration: Duration(milliseconds: widget.duration),
153 | curve: widget.curve);
154 | }
155 | });
156 | }
157 | }
158 |
159 | void cancelTime(){
160 | timer?.cancel();
161 | timer = null;
162 | }
163 |
164 | void restData(){
165 | for (int i = 0;i<2;i++){
166 | bannerList.addAll(widget.data);
167 | }
168 | }
169 |
170 | int getRealCount() => widget.data.length;
171 |
172 | @override
173 | void dispose() {
174 | cancelTime();
175 | controller?.dispose();
176 | controller = null;
177 | super.dispose();
178 | }
179 | }
180 |
181 | class BannerItem extends StatefulWidget{
182 | final String url;
183 | BannerItem({
184 | Key key,
185 | @required this.url,
186 | }): super(key : key);
187 |
188 | @override
189 | createState() => ItemState();
190 | }
191 |
192 | class ItemState extends State {
193 | @override
194 | Widget build(BuildContext context) {
195 | return Container(
196 | height: 160.0,
197 | width: double.infinity,
198 | color: Colors.black38,
199 | child: Image.network(widget.url, fit: BoxFit.cover,),
200 | );
201 | }
202 | }
203 |
204 | //Banner中滚动的Item
205 | typedef Widget ItemBuild(int position);
206 |
207 | //在这添加自定义的指示器,指示器位置自定义请使用Align部件,默认圆点
208 | typedef Widget IndicatorBuild(int position,int counmt);
209 |
210 | //点击监听
211 | typedef void OnClick(int position, BannerWithEval bannerWithEval);
--------------------------------------------------------------------------------
/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: juejin_flutter
2 | description: juejin client power by flutter
3 |
4 | # The following defines the version and build number for your application.
5 | # A version number is three numbers separated by dots, like 1.2.43
6 | # followed by an optional build number separated by a +.
7 | # Both the version and the builder number may be overridden in flutter
8 | # build by specifying --build-name and --build-number, respectively.
9 | # Read more about versioning at semver.org.
10 | version: 1.0.0+1
11 |
12 | environment:
13 | sdk: ">=2.0.0-dev.68.0 <3.0.0"
14 |
15 | dependencies:
16 | flutter:
17 | sdk: flutter
18 |
19 | # The following adds the Cupertino Icons font to your application.
20 | # Use with the CupertinoIcons class for iOS style icons.
21 | cupertino_icons: ^0.1.2
22 | flutter_webview_plugin: ^0.2.1+2
23 | http: ^0.11.3
24 |
25 | dev_dependencies:
26 | flutter_test:
27 | sdk: flutter
28 |
29 |
30 | # For information on the generic Dart part of this file, see the
31 | # following page: https://www.dartlang.org/tools/pub/pubspec
32 |
33 | # The following section is specific to Flutter.
34 | flutter:
35 |
36 | # The following line ensures that the Material Icons font is
37 | # included with your application, so that you can use the icons in
38 | # the material Icons class.
39 | uses-material-design: true
40 |
41 | # To add assets to your application, add an assets section, like this:
42 | assets:
43 | - assets/splash_bg.webp
44 | - assets/splash_logo.png
45 | - assets/tab_home.png
46 | - assets/tab_home_normal.png
47 | - assets/tab_activity.png
48 | - assets/tab_activity_normal.png
49 | - assets/tab_explore.png
50 | - assets/tab_explore_normal.png
51 | - assets/tab_xiaoce.png
52 | - assets/tab_xiaoce_normal.png
53 | - assets/tab_profile.png
54 | - assets/tab_profile_normal.png
55 | - assets/timeline_comment.png
56 | - assets/timeline_like_normal.png
57 | - assets/profile_arrow.png
58 | - assets/loading.png
59 |
60 | # An image asset can refer to one or more resolution-specific "variants", see
61 | # https://flutter.io/assets-and-images/#resolution-aware.
62 |
63 | # For details regarding adding assets from package dependencies, see
64 | # https://flutter.io/assets-and-images/#from-packages
65 |
66 | # To add custom fonts to your application, add a fonts section here,
67 | # in this "flutter" section. Each entry in this list should have a
68 | # "family" key with the font family name, and a "fonts" key with a
69 | # list giving the asset and other descriptors for the font. For
70 | # example:
71 | # fonts:
72 | # - family: Schyler
73 | # fonts:
74 | # - asset: fonts/Schyler-Regular.ttf
75 | # - asset: fonts/Schyler-Italic.ttf
76 | # style: italic
77 | # - family: Trajan Pro
78 | # fonts:
79 | # - asset: fonts/TrajanPro.ttf
80 | # - asset: fonts/TrajanPro_Bold.ttf
81 | # weight: 700
82 | #
83 | # For details regarding fonts from package dependencies,
84 | # see https://flutter.io/custom-fonts/#from-packages
85 |
--------------------------------------------------------------------------------
/screenshots/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hanks-zyh/juejin_flutter/5dcef5b1ec5e084f3dc60350e5fd9d7309f4742b/screenshots/1.png
--------------------------------------------------------------------------------
/screenshots/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hanks-zyh/juejin_flutter/5dcef5b1ec5e084f3dc60350e5fd9d7309f4742b/screenshots/2.png
--------------------------------------------------------------------------------
/screenshots/3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hanks-zyh/juejin_flutter/5dcef5b1ec5e084f3dc60350e5fd9d7309f4742b/screenshots/3.png
--------------------------------------------------------------------------------
/screenshots/4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hanks-zyh/juejin_flutter/5dcef5b1ec5e084f3dc60350e5fd9d7309f4742b/screenshots/4.png
--------------------------------------------------------------------------------
/screenshots/5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hanks-zyh/juejin_flutter/5dcef5b1ec5e084f3dc60350e5fd9d7309f4742b/screenshots/5.png
--------------------------------------------------------------------------------
/screenshots/demo.apk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hanks-zyh/juejin_flutter/5dcef5b1ec5e084f3dc60350e5fd9d7309f4742b/screenshots/demo.apk
--------------------------------------------------------------------------------
/screenshots/demo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hanks-zyh/juejin_flutter/5dcef5b1ec5e084f3dc60350e5fd9d7309f4742b/screenshots/demo.gif
--------------------------------------------------------------------------------
/screenshots/qr.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hanks-zyh/juejin_flutter/5dcef5b1ec5e084f3dc60350e5fd9d7309f4742b/screenshots/qr.png
--------------------------------------------------------------------------------
/test/widget_test.dart:
--------------------------------------------------------------------------------
1 | // This is a basic Flutter widget test.
2 | // To perform an interaction with a widget in your test, use the WidgetTester utility that Flutter
3 | // provides. For example, you can send tap and scroll gestures. You can also use WidgetTester to
4 | // find child widgets in the widget tree, read text, and verify that the values of widget properties
5 | // are correct.
6 |
7 | import 'package:flutter/material.dart';
8 | import 'package:flutter_test/flutter_test.dart';
9 |
10 | import 'package:juejin_flutter/main.dart';
11 |
12 | void main() {
13 | testWidgets('Counter increments smoke test', (WidgetTester tester) async {
14 |
15 | });
16 | }
17 |
--------------------------------------------------------------------------------