├── .all-contributorsrc
├── .github
├── FUNDING.yml
├── ISSUE_TEMPLATE
│ ├── bug-report--bug---.md
│ ├── config.yml
│ └── feature-request-------.md
├── jetbrains-variant.png
├── no-response.yml
└── workflows
│ ├── issue_labeler.yml
│ └── runnable.yml
├── .gitignore
├── .metadata
├── .run
├── Generate localizations on MacOS_Linux.run.xml
├── Generate localizations on Windows.run.xml
└── Generate models.run.xml
├── CODEOWNERS
├── LICENSE
├── README.md
├── analysis_options.yaml
├── android
├── .gitignore
├── app
│ ├── build.gradle
│ └── src
│ │ └── main
│ │ ├── AndroidManifest.xml
│ │ ├── ic_launcher-playstore.png
│ │ ├── kotlin
│ │ └── com
│ │ │ └── fluttercandies
│ │ │ └── juejin
│ │ │ └── MainActivity.kt
│ │ └── res
│ │ ├── drawable-v21
│ │ └── launch_background.xml
│ │ ├── drawable
│ │ ├── ic_launcher_foreground.xml
│ │ └── launch_background.xml
│ │ ├── mipmap-anydpi-v26
│ │ ├── ic_launcher.xml
│ │ └── ic_launcher_round.xml
│ │ ├── mipmap-hdpi
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-mdpi
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xhdpi
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxhdpi
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxxhdpi
│ │ └── ic_launcher_round.png
│ │ ├── values-night
│ │ └── styles.xml
│ │ └── values
│ │ ├── ic_launcher_background.xml
│ │ └── styles.xml
├── build.gradle
├── gradle.properties
├── gradle
│ └── wrapper
│ │ └── gradle-wrapper.properties
├── key.jks
└── settings.gradle
├── assets
├── brand-with-text.svg
├── brand.svg
└── icon
│ ├── post
│ └── hot_comment.png
│ ├── topic
│ └── pin_detail_head_bg.webp
│ └── user
│ ├── jy_lv1.webp
│ ├── jy_lv2.webp
│ ├── jy_lv3.webp
│ ├── jy_lv4.webp
│ ├── jy_lv5.webp
│ ├── jy_lv6.webp
│ ├── jy_lv7.webp
│ ├── jy_lv8.webp
│ ├── lv1.webp
│ ├── lv2.webp
│ ├── lv3.webp
│ ├── lv4.webp
│ ├── lv5.webp
│ ├── lv6.webp
│ ├── lv7.webp
│ ├── lv8.webp
│ ├── vip_lv1.webp
│ ├── vip_lv2.webp
│ ├── vip_lv3.webp
│ ├── vip_lv4.webp
│ └── vip_lv5.webp
├── build.yaml
├── ff_annotation_route_commands
├── ios
├── .gitignore
├── Flutter
│ ├── AppFrameworkInfo.plist
│ ├── Debug.xcconfig
│ └── Release.xcconfig
├── Podfile
├── Runner.xcodeproj
│ ├── project.pbxproj
│ ├── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ │ ├── IDEWorkspaceChecks.plist
│ │ │ └── WorkspaceSettings.xcsettings
│ └── xcshareddata
│ │ └── xcschemes
│ │ └── Runner.xcscheme
├── Runner.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ ├── IDEWorkspaceChecks.plist
│ │ └── WorkspaceSettings.xcsettings
└── Runner
│ ├── AppDelegate.swift
│ ├── Assets.xcassets
│ ├── AppIcon.appiconset
│ │ ├── Contents.json
│ │ ├── icon-1024.png
│ │ ├── icon-20-ipad.png
│ │ ├── icon-20@2x-ipad.png
│ │ ├── icon-20@2x.png
│ │ ├── icon-20@3x.png
│ │ ├── icon-29-ipad.png
│ │ ├── icon-29.png
│ │ ├── icon-29@2x-ipad.png
│ │ ├── icon-29@2x.png
│ │ ├── icon-29@3x.png
│ │ ├── icon-40.png
│ │ ├── icon-40@2x.png
│ │ ├── icon-40@3x.png
│ │ ├── icon-60@2x.png
│ │ ├── icon-60@3x.png
│ │ ├── icon-76.png
│ │ ├── icon-76@2x.png
│ │ └── icon-83.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
├── l10n.yaml
├── lib
├── apis
│ ├── content_api.dart
│ ├── interact_api.dart
│ ├── passport_api.dart
│ ├── recommend_api.dart
│ └── tag_api.dart
├── app.dart
├── constants
│ ├── constants.dart
│ ├── resources.dart
│ ├── screens.dart
│ ├── styles.dart
│ └── themes.dart
├── exports.dart
├── extensions
│ ├── brightness_extension.dart
│ ├── build_context_extension.dart
│ ├── color_extension.dart
│ ├── duration_extension.dart
│ ├── future_extension.dart
│ ├── num_extension.dart
│ ├── state_extension.dart
│ └── string_extension.dart
├── internals
│ ├── instance.dart
│ ├── methods.dart
│ ├── navigator_observer.dart
│ ├── special_text.dart
│ └── urls.dart
├── l10n
│ ├── app_en.arb
│ ├── app_zh.arb
│ └── gen
│ │ ├── jj_localizations.dart
│ │ ├── jj_localizations_en.dart
│ │ └── jj_localizations_zh.dart
├── main.dart
├── models
│ ├── data_model.d.dart
│ ├── data_model.dart
│ ├── data_model.g.dart
│ ├── feed_model.dart
│ ├── item
│ │ ├── advertise_item_model.dart
│ │ ├── article_item_model.dart
│ │ ├── comment_item_model.dart
│ │ └── post_item_model.dart
│ ├── loading_base.dart
│ ├── response_model.dart
│ └── user_model.dart
├── pages
│ ├── article
│ │ └── detail.dart
│ ├── club
│ │ └── club.dart
│ ├── components
│ │ └── comments.dart
│ ├── home.dart
│ ├── home
│ │ ├── articles.dart
│ │ ├── mine.dart
│ │ └── pins.dart
│ └── splash.dart
├── routes
│ ├── juejin_route.dart
│ ├── juejin_routes.dart
│ └── page_route.dart
├── utils
│ ├── cache_util.dart
│ ├── device_util.dart
│ ├── haptic_util.dart
│ ├── http_util.dart
│ ├── log_util.dart
│ ├── package_util.dart
│ └── toast_util.dart
└── widgets
│ ├── _none.dart
│ ├── error_widget.dart
│ ├── gaps.dart
│ ├── lazy_indexed_stack.dart
│ ├── logo.dart
│ ├── refresh
│ ├── base_refresh_wrapper.dart
│ ├── pull_to_refresh_header.dart
│ ├── refresh_grid_wrapper.dart
│ └── refresh_list_wrapper.dart
│ ├── tapper.dart
│ └── webview.dart
├── linux
├── .gitignore
├── CMakeLists.txt
├── flutter
│ ├── CMakeLists.txt
│ ├── generated_plugin_registrant.cc
│ ├── generated_plugin_registrant.h
│ └── generated_plugins.cmake
├── main.cc
├── my_application.cc
└── my_application.h
├── macos
├── .gitignore
├── Flutter
│ ├── Flutter-Debug.xcconfig
│ ├── Flutter-Release.xcconfig
│ └── GeneratedPluginRegistrant.swift
├── Podfile
├── Podfile.lock
├── Runner.xcodeproj
│ ├── project.pbxproj
│ ├── project.xcworkspace
│ │ └── xcshareddata
│ │ │ └── IDEWorkspaceChecks.plist
│ └── xcshareddata
│ │ └── xcschemes
│ │ └── Runner.xcscheme
├── Runner.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
└── Runner
│ ├── AppDelegate.swift
│ ├── Assets.xcassets
│ └── AppIcon.appiconset
│ │ ├── Contents.json
│ │ ├── app_icon_1024.png
│ │ ├── app_icon_128.png
│ │ ├── app_icon_16.png
│ │ ├── app_icon_256.png
│ │ ├── app_icon_32.png
│ │ ├── app_icon_512.png
│ │ └── app_icon_64.png
│ ├── Base.lproj
│ └── MainMenu.xib
│ ├── Configs
│ ├── AppInfo.xcconfig
│ ├── Debug.xcconfig
│ ├── Release.xcconfig
│ └── Warnings.xcconfig
│ ├── DebugProfile.entitlements
│ ├── Info.plist
│ ├── MainFlutterWindow.swift
│ └── Release.entitlements
├── pubspec.yaml
├── test
└── api_test.dart
├── tools
├── internals
│ ├── command.dart
│ ├── constants.dart
│ ├── defs.dart
│ └── utils.dart
├── jj.dart
└── src
│ ├── models.dart
│ ├── release.dart
│ └── version.dart
└── windows
├── .gitignore
├── CMakeLists.txt
├── flutter
├── CMakeLists.txt
├── generated_plugin_registrant.cc
├── generated_plugin_registrant.h
└── generated_plugins.cmake
└── runner
├── CMakeLists.txt
├── Runner.rc
├── flutter_window.cpp
├── flutter_window.h
├── main.cpp
├── resource.h
├── resources
└── app_icon.ico
├── runner.exe.manifest
├── utils.cpp
├── utils.h
├── win32_window.cpp
└── win32_window.h
/.all-contributorsrc:
--------------------------------------------------------------------------------
1 | {
2 | "files": [
3 | "README.md"
4 | ],
5 | "imageSize": 100,
6 | "commit": false,
7 | "contributors": [
8 | {
9 | "login": "AlexV525",
10 | "name": "Alex Li",
11 | "avatar_url": "https://avatars1.githubusercontent.com/u/15884415?v=4",
12 | "profile": "https://blog.alexv525.com",
13 | "contributions": [
14 | "code",
15 | "design",
16 | "doc",
17 | "example",
18 | "ideas",
19 | "maintenance",
20 | "question",
21 | "review"
22 | ]
23 | },
24 | {
25 | "login": "a1017480401",
26 | "name": "友人A",
27 | "avatar_url": "https://avatars.githubusercontent.com/u/58846244?v=4",
28 | "profile": "https://github.com/a1017480401",
29 | "contributions": [
30 | "code"
31 | ]
32 | },
33 | {
34 | "login": "shirne",
35 | "name": "shirne",
36 | "avatar_url": "https://avatars.githubusercontent.com/u/2263157?v=4",
37 | "profile": "https://www.shirne.com/",
38 | "contributions": [
39 | "code",
40 | "bug",
41 | "translation"
42 | ]
43 | },
44 | {
45 | "login": "MrDgbot",
46 | "name": "MrDgbot",
47 | "avatar_url": "https://avatars.githubusercontent.com/u/60038945?v=4",
48 | "profile": "https://github.com/MrDgbot",
49 | "contributions": [
50 | "code"
51 | ]
52 | },
53 | {
54 | "login": "DemoJameson",
55 | "name": "DemoJameson",
56 | "avatar_url": "https://avatars.githubusercontent.com/u/181192?v=4",
57 | "profile": "http://www.demojameson.com",
58 | "contributions": [
59 | "code",
60 | "bug"
61 | ]
62 | },
63 | {
64 | "login": "WeiJun0507",
65 | "name": "Wei Jun",
66 | "avatar_url": "https://avatars.githubusercontent.com/u/66726409?v=4",
67 | "profile": "https://github.com/WeiJun0507",
68 | "contributions": [
69 | "code"
70 | ]
71 | }
72 | ],
73 | "contributorsPerLine": 7,
74 | "projectName": "flutter_juejin",
75 | "projectOwner": "fluttercandies",
76 | "repoType": "github",
77 | "repoHost": "https://github.com",
78 | "skipCi": true
79 | }
80 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
4 | patreon: # Replace with a single Patreon username
5 | open_collective: # Replace with a single Open Collective username
6 | ko_fi: # Replace with a single Ko-fi username
7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9 | liberapay: # Replace with a single Liberapay username
10 | issuehunt: # Replace with a single IssueHunt username
11 | otechie: # Replace with a single Otechie username
12 | custom: ['https://www.paypal.me/AlexV525', 'https://www.alexv525.com/wechat.png', 'https://www.alexv525.com/alipay.jpg']
13 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug-report--bug---.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report (BUG模板)
3 | about: Create a bug report helping us fix it. (创建一个 BUG 报告以帮助我们进行修复)
4 | title: "[BUG] Error with something"
5 | labels: await investigate, bug
6 |
7 | ---
8 |
9 | **Describe the bug**
10 |
12 |
13 | **How to reproduce**
14 |
19 |
20 | Steps to reproduce the behavior:
21 |
22 |
23 | 1. Go to '...'
24 | 2. Click on '....'
25 | 3. Scroll down to '....'
26 | 4. Error occurred.
27 |
28 | **Expected behavior**
29 |
31 |
32 | **Screenshots (If contains)**
33 |
35 |
36 | **Version information**
37 |
38 | - Device: [e.g. iPhone X]
39 | - OS: [e.g. iOS 14.7.1]
40 | - Flutter Version: [e.g. v3.0.0]
41 |
42 | **Additional context**
43 |
45 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/config.yml:
--------------------------------------------------------------------------------
1 | blank_issues_enabled: false
2 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature-request-------.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request (功能请求)
3 | about: Request a new feature that the package didn't include. (请求一个依赖并未包含的功能)
4 | title: "[Feature] Request a feature with something"
5 | labels: feature, await investigate
6 |
7 | ---
8 |
9 | **Is your feature request related to a problem?**
10 |
12 |
13 | **Describe the solution you'd like**
14 |
16 |
17 | **Describe alternatives you've considered**
18 |
21 |
22 | **Additional context**
23 |
25 |
26 | **Version information**
27 |
28 | - Device: [e.g. iPhone X]
29 | - OS: [e.g. iOS 14.7.1]
30 | - Flutter Version: [e.g. v3.0.0]
31 |
--------------------------------------------------------------------------------
/.github/jetbrains-variant.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fluttercandies/flutter_juejin/09a5c2de8b882dc46c1b6b79ce8cf1070a05bdaa/.github/jetbrains-variant.png
--------------------------------------------------------------------------------
/.github/no-response.yml:
--------------------------------------------------------------------------------
1 | # Configuration for probot-no-response - https://github.com/probot/no-response
2 |
3 | # Number of days of inactivity before an Issue is closed for lack of response
4 | daysUntilClose: 14
5 | # Label requiring a response
6 | responseRequiredLabel: "await response"
7 | # Comment to post when closing an Issue for lack of response. Set to `false` to disable
8 | closeComment: >
9 | This issue has been automatically closed because there has been no response
10 | to our request for more information from the original author. Please reach
11 | out if you have or find the answers we need so that we can investigate further.
12 |
--------------------------------------------------------------------------------
/.github/workflows/issue_labeler.yml:
--------------------------------------------------------------------------------
1 | name: "Issue Labeler"
2 |
3 | on:
4 | issues:
5 | types: [ opened ]
6 |
7 | jobs:
8 | label:
9 | runs-on: ubuntu-latest
10 | steps:
11 | - name: Auto label
12 | uses: Renato66/auto-label@v2.2.0
13 | with:
14 | repo-token: ${{ secrets.GITHUB_TOKEN }}
15 | ignore-comments: true
16 | default-labels: '["await triage"]'
17 |
--------------------------------------------------------------------------------
/.github/workflows/runnable.yml:
--------------------------------------------------------------------------------
1 | name: Runnable (stable)
2 |
3 | on:
4 | pull_request:
5 | branches:
6 | - main
7 |
8 | jobs:
9 | analyze:
10 | name: Analyze on ${{ matrix.os }}
11 | runs-on: ${{ matrix.os }}
12 | strategy:
13 | matrix:
14 | os: [ ubuntu-latest ]
15 | steps:
16 | - uses: actions/checkout@v3
17 | - uses: actions/setup-java@v3
18 | with:
19 | distribution: 'adopt'
20 | java-version: '11.x'
21 | - uses: subosito/flutter-action@v2
22 | with:
23 | channel: 'stable'
24 | - name: Log Dart/Flutter versions
25 | run: |
26 | dart --version
27 | flutter --version
28 | - name: Prepare dependencies
29 | run: flutter pub get
30 | - name: Analyse the repo
31 | run: flutter analyze lib
32 |
33 | test_iOS:
34 | needs: analyze
35 | name: Test iOS on ${{ matrix.os }}
36 | runs-on: ${{ matrix.os }}
37 | strategy:
38 | matrix:
39 | os: [ macos-latest ]
40 | steps:
41 | - uses: actions/checkout@v3
42 | - uses: actions/setup-java@v3
43 | with:
44 | distribution: 'adopt'
45 | java-version: '11.x'
46 | - uses: subosito/flutter-action@v2
47 | with:
48 | architecture: x64
49 | channel: 'stable'
50 | - run: dart --version
51 | - run: flutter --version
52 | - run: flutter pub get
53 | - run: flutter build ios --no-codesign
54 |
55 | test_android:
56 | needs: analyze
57 | name: Test Android on ${{ matrix.os }}
58 | runs-on: ${{ matrix.os }}
59 | strategy:
60 | matrix:
61 | os: [ ubuntu-latest ]
62 | steps:
63 | - uses: actions/checkout@v3
64 | - uses: actions/setup-java@v3
65 | with:
66 | distribution: 'adopt'
67 | java-version: '11.x'
68 | - uses: subosito/flutter-action@v2
69 | with:
70 | channel: 'stable'
71 | - run: dart --version
72 | - run: flutter --version
73 | - run: flutter pub get
74 | - run: sudo echo "y" | sudo $ANDROID_HOME/tools/bin/sdkmanager "ndk;21.4.7075529"
75 | - run: flutter build apk --debug
76 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Miscellaneous
2 | *.class
3 | *.log
4 | *.pyc
5 | *.swp
6 | .DS_Store
7 | .atom/
8 | .buildlog/
9 | .history
10 | .svn/
11 | migrate_working_dir/
12 | release/
13 |
14 | # IntelliJ related
15 | *.iml
16 | *.ipr
17 | *.iws
18 | .idea/
19 |
20 | # The .vscode folder contains launch configuration and tasks you configure in
21 | # VS Code which you may wish to be included in version control, so this line
22 | # is commented out by default.
23 | #.vscode/
24 |
25 | # Flutter/Dart/Pub related
26 | **/doc/api/
27 | **/ios/Flutter/.last_build_id
28 | .dart_tool/
29 | .flutter-plugins
30 | .flutter-plugins-dependencies
31 | .packages
32 | .pub-cache/
33 | .pub/
34 | /build/
35 | pubspec.lock
36 | lib/l10n/gen/jj_localizations_untranslated.txt
37 |
38 | # Web related
39 | lib/generated_plugin_registrant.dart
40 |
41 | # Symbolication related
42 | app.*.symbols
43 |
44 | # Obfuscation related
45 | app.*.map.json
46 |
47 | # Android Studio will place build artifacts here
48 | /android/app/debug
49 | /android/app/profile
50 | /android/app/release
51 |
--------------------------------------------------------------------------------
/.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.
5 |
6 | version:
7 | revision: 762bd96f38711baf2672b8bccb0b75af46425806
8 | channel: stable
9 |
10 | project_type: app
11 |
12 | # Tracks metadata for the flutter migrate command
13 | migration:
14 | platforms:
15 | - platform: root
16 | create_revision: 762bd96f38711baf2672b8bccb0b75af46425806
17 | base_revision: 762bd96f38711baf2672b8bccb0b75af46425806
18 | - platform: android
19 | create_revision: 762bd96f38711baf2672b8bccb0b75af46425806
20 | base_revision: 762bd96f38711baf2672b8bccb0b75af46425806
21 | - platform: ios
22 | create_revision: 762bd96f38711baf2672b8bccb0b75af46425806
23 | base_revision: 762bd96f38711baf2672b8bccb0b75af46425806
24 | - platform: linux
25 | create_revision: 762bd96f38711baf2672b8bccb0b75af46425806
26 | base_revision: 762bd96f38711baf2672b8bccb0b75af46425806
27 | - platform: macos
28 | create_revision: 762bd96f38711baf2672b8bccb0b75af46425806
29 | base_revision: 762bd96f38711baf2672b8bccb0b75af46425806
30 | - platform: web
31 | create_revision: 762bd96f38711baf2672b8bccb0b75af46425806
32 | base_revision: 762bd96f38711baf2672b8bccb0b75af46425806
33 | - platform: windows
34 | create_revision: 762bd96f38711baf2672b8bccb0b75af46425806
35 | base_revision: 762bd96f38711baf2672b8bccb0b75af46425806
36 |
37 | # User provided section
38 |
39 | # List of Local paths (relative to this file) that should be
40 | # ignored by the migrate tool.
41 | #
42 | # Files that are not part of the templates will be ignored by default.
43 | unmanaged_files:
44 | - 'lib/main.dart'
45 | - 'ios/Runner.xcodeproj/project.pbxproj'
46 |
--------------------------------------------------------------------------------
/.run/Generate localizations on MacOS_Linux.run.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/.run/Generate localizations on Windows.run.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/.run/Generate models.run.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/CODEOWNERS:
--------------------------------------------------------------------------------
1 | * @AlexV525
2 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | ACADEMIC PUBLIC LICENSE
2 | version 1.1
3 |
4 | Copyright (C) 2022 FlutterCandies
5 |
6 | This license contains the terms and conditions of using flutter_juejin in
7 | noncommercial settings: at academic institutions for teaching and research
8 | use and for personal or educational purposes.
9 |
10 | END OF TERMS AND CONDITIONS
11 |
--------------------------------------------------------------------------------
/analysis_options.yaml:
--------------------------------------------------------------------------------
1 | include: package:flutter_lints/flutter.yaml
2 |
3 | analyzer:
4 | exclude:
5 | - 'lib/l10n/gen/*.dart'
6 | - "lib/models/**.g.dart"
7 | - "lib/routes/juejin_route**.dart"
8 |
9 | linter:
10 | rules:
11 | always_declare_return_types: true
12 | avoid_bool_literals_in_conditional_expressions: true
13 | avoid_classes_with_only_static_members: true
14 | avoid_print: true
15 | avoid_redundant_argument_values: true
16 | avoid_slow_async_io: true
17 | avoid_void_async: true
18 | constant_identifier_names: false
19 | directives_ordering: true
20 | prefer_const_constructors: true
21 | prefer_const_constructors_in_immutables: true
22 | prefer_const_declarations: true
23 | prefer_const_literals_to_create_immutables: true
24 | prefer_single_quotes: true
25 | require_trailing_commas: true
26 | sort_child_properties_last: true
27 | sort_constructors_first: true
28 | sort_pub_dependencies: true
29 | sort_unnamed_constructors_first: true
30 |
--------------------------------------------------------------------------------
/android/.gitignore:
--------------------------------------------------------------------------------
1 | gradle-wrapper.jar
2 | /.gradle
3 | /captures/
4 | /gradlew
5 | /gradlew.bat
6 | /local.properties
7 | GeneratedPluginRegistrant.java
8 |
9 | # Remember to never publicly share your keystore.
10 | # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
11 | key.properties
12 |
--------------------------------------------------------------------------------
/android/app/build.gradle:
--------------------------------------------------------------------------------
1 | def localProperties = new Properties()
2 | def localPropertiesFile = rootProject.file('local.properties')
3 | if (localPropertiesFile.exists()) {
4 | localPropertiesFile.withReader('UTF-8') { reader ->
5 | localProperties.load(reader)
6 | }
7 | }
8 |
9 | def flutterRoot = localProperties.getProperty('flutter.sdk')
10 | if (flutterRoot == null) {
11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
12 | }
13 |
14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
15 | if (flutterVersionCode == null) {
16 | flutterVersionCode = '1'
17 | }
18 |
19 | def flutterVersionName = localProperties.getProperty('flutter.versionName')
20 | if (flutterVersionName == null) {
21 | flutterVersionName = '1.0'
22 | }
23 |
24 | apply plugin: 'com.android.application'
25 | apply plugin: 'kotlin-android'
26 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
27 |
28 | android {
29 | compileSdkVersion 33
30 | ndkVersion flutter.ndkVersion
31 |
32 | sourceSets {
33 | main.java.srcDirs += 'src/main/kotlin'
34 | }
35 |
36 | defaultConfig {
37 | applicationId "com.fluttercandies.juejin"
38 | minSdkVersion 21
39 | targetSdkVersion flutter.targetSdkVersion
40 | versionCode flutterVersionCode.toInteger()
41 | versionName flutterVersionName
42 | }
43 |
44 | signingConfigs {
45 | forAll {
46 | storeFile file("${rootDir.absolutePath}/key.jks")
47 | storePassword 'juejin'
48 | keyAlias 'juejin'
49 | keyPassword 'juejin'
50 | }
51 | }
52 |
53 | buildTypes {
54 | debug {
55 | signingConfig signingConfigs.forAll
56 | }
57 | profile {
58 | signingConfig signingConfigs.forAll
59 | }
60 | release {
61 | signingConfig signingConfigs.forAll
62 | }
63 | }
64 | }
65 |
66 | flutter {
67 | source '../..'
68 | }
69 |
70 | dependencies {}
71 |
--------------------------------------------------------------------------------
/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
8 |
16 |
20 |
24 |
25 |
26 |
27 |
28 |
29 |
31 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/android/app/src/main/ic_launcher-playstore.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fluttercandies/flutter_juejin/09a5c2de8b882dc46c1b6b79ce8cf1070a05bdaa/android/app/src/main/ic_launcher-playstore.png
--------------------------------------------------------------------------------
/android/app/src/main/kotlin/com/fluttercandies/juejin/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.fluttercandies.juejin
2 |
3 | import io.flutter.embedding.android.FlutterActivity
4 |
5 | class MainActivity: FlutterActivity() {
6 | }
7 |
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-v21/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
6 |
10 |
13 |
16 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fluttercandies/flutter_juejin/09a5c2de8b882dc46c1b6b79ce8cf1070a05bdaa/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fluttercandies/flutter_juejin/09a5c2de8b882dc46c1b6b79ce8cf1070a05bdaa/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fluttercandies/flutter_juejin/09a5c2de8b882dc46c1b6b79ce8cf1070a05bdaa/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fluttercandies/flutter_juejin/09a5c2de8b882dc46c1b6b79ce8cf1070a05bdaa/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fluttercandies/flutter_juejin/09a5c2de8b882dc46c1b6b79ce8cf1070a05bdaa/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/android/app/src/main/res/values-night/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
15 |
18 |
19 |
--------------------------------------------------------------------------------
/android/app/src/main/res/values/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #FFFFFF
4 |
--------------------------------------------------------------------------------
/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
16 |
19 |
20 |
--------------------------------------------------------------------------------
/android/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | ext.kotlin_version = '1.7.20'
3 | repositories {
4 | google()
5 | mavenCentral()
6 | }
7 |
8 | dependencies {
9 | classpath 'com.android.tools.build:gradle:7.1.2'
10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
11 | }
12 | }
13 |
14 | allprojects {
15 | repositories {
16 | google()
17 | mavenCentral()
18 | }
19 | }
20 |
21 | rootProject.buildDir = '../build'
22 | subprojects {
23 | project.buildDir = "${rootProject.buildDir}/${project.name}"
24 | }
25 | subprojects {
26 | project.evaluationDependsOn(':app')
27 | }
28 |
29 | tasks.register("clean", Delete) {
30 | delete rootProject.buildDir
31 | }
32 |
--------------------------------------------------------------------------------
/android/gradle.properties:
--------------------------------------------------------------------------------
1 | org.gradle.jvmargs=-Xmx1536M
2 | android.useAndroidX=true
3 | android.enableJetifier=true
4 |
--------------------------------------------------------------------------------
/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Fri Jun 23 08:50:38 CEST 2017
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-all.zip
7 |
--------------------------------------------------------------------------------
/android/key.jks:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fluttercandies/flutter_juejin/09a5c2de8b882dc46c1b6b79ce8cf1070a05bdaa/android/key.jks
--------------------------------------------------------------------------------
/android/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
3 | def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
4 | def properties = new Properties()
5 |
6 | assert localPropertiesFile.exists()
7 | localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
8 |
9 | def flutterSdkPath = properties.getProperty("flutter.sdk")
10 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
11 | apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"
12 |
--------------------------------------------------------------------------------
/assets/brand-with-text.svg:
--------------------------------------------------------------------------------
1 |
17 |
--------------------------------------------------------------------------------
/assets/brand.svg:
--------------------------------------------------------------------------------
1 |
6 |
--------------------------------------------------------------------------------
/assets/icon/post/hot_comment.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fluttercandies/flutter_juejin/09a5c2de8b882dc46c1b6b79ce8cf1070a05bdaa/assets/icon/post/hot_comment.png
--------------------------------------------------------------------------------
/assets/icon/topic/pin_detail_head_bg.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fluttercandies/flutter_juejin/09a5c2de8b882dc46c1b6b79ce8cf1070a05bdaa/assets/icon/topic/pin_detail_head_bg.webp
--------------------------------------------------------------------------------
/assets/icon/user/jy_lv1.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fluttercandies/flutter_juejin/09a5c2de8b882dc46c1b6b79ce8cf1070a05bdaa/assets/icon/user/jy_lv1.webp
--------------------------------------------------------------------------------
/assets/icon/user/jy_lv2.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fluttercandies/flutter_juejin/09a5c2de8b882dc46c1b6b79ce8cf1070a05bdaa/assets/icon/user/jy_lv2.webp
--------------------------------------------------------------------------------
/assets/icon/user/jy_lv3.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fluttercandies/flutter_juejin/09a5c2de8b882dc46c1b6b79ce8cf1070a05bdaa/assets/icon/user/jy_lv3.webp
--------------------------------------------------------------------------------
/assets/icon/user/jy_lv4.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fluttercandies/flutter_juejin/09a5c2de8b882dc46c1b6b79ce8cf1070a05bdaa/assets/icon/user/jy_lv4.webp
--------------------------------------------------------------------------------
/assets/icon/user/jy_lv5.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fluttercandies/flutter_juejin/09a5c2de8b882dc46c1b6b79ce8cf1070a05bdaa/assets/icon/user/jy_lv5.webp
--------------------------------------------------------------------------------
/assets/icon/user/jy_lv6.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fluttercandies/flutter_juejin/09a5c2de8b882dc46c1b6b79ce8cf1070a05bdaa/assets/icon/user/jy_lv6.webp
--------------------------------------------------------------------------------
/assets/icon/user/jy_lv7.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fluttercandies/flutter_juejin/09a5c2de8b882dc46c1b6b79ce8cf1070a05bdaa/assets/icon/user/jy_lv7.webp
--------------------------------------------------------------------------------
/assets/icon/user/jy_lv8.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fluttercandies/flutter_juejin/09a5c2de8b882dc46c1b6b79ce8cf1070a05bdaa/assets/icon/user/jy_lv8.webp
--------------------------------------------------------------------------------
/assets/icon/user/lv1.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fluttercandies/flutter_juejin/09a5c2de8b882dc46c1b6b79ce8cf1070a05bdaa/assets/icon/user/lv1.webp
--------------------------------------------------------------------------------
/assets/icon/user/lv2.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fluttercandies/flutter_juejin/09a5c2de8b882dc46c1b6b79ce8cf1070a05bdaa/assets/icon/user/lv2.webp
--------------------------------------------------------------------------------
/assets/icon/user/lv3.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fluttercandies/flutter_juejin/09a5c2de8b882dc46c1b6b79ce8cf1070a05bdaa/assets/icon/user/lv3.webp
--------------------------------------------------------------------------------
/assets/icon/user/lv4.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fluttercandies/flutter_juejin/09a5c2de8b882dc46c1b6b79ce8cf1070a05bdaa/assets/icon/user/lv4.webp
--------------------------------------------------------------------------------
/assets/icon/user/lv5.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fluttercandies/flutter_juejin/09a5c2de8b882dc46c1b6b79ce8cf1070a05bdaa/assets/icon/user/lv5.webp
--------------------------------------------------------------------------------
/assets/icon/user/lv6.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fluttercandies/flutter_juejin/09a5c2de8b882dc46c1b6b79ce8cf1070a05bdaa/assets/icon/user/lv6.webp
--------------------------------------------------------------------------------
/assets/icon/user/lv7.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fluttercandies/flutter_juejin/09a5c2de8b882dc46c1b6b79ce8cf1070a05bdaa/assets/icon/user/lv7.webp
--------------------------------------------------------------------------------
/assets/icon/user/lv8.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fluttercandies/flutter_juejin/09a5c2de8b882dc46c1b6b79ce8cf1070a05bdaa/assets/icon/user/lv8.webp
--------------------------------------------------------------------------------
/assets/icon/user/vip_lv1.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fluttercandies/flutter_juejin/09a5c2de8b882dc46c1b6b79ce8cf1070a05bdaa/assets/icon/user/vip_lv1.webp
--------------------------------------------------------------------------------
/assets/icon/user/vip_lv2.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fluttercandies/flutter_juejin/09a5c2de8b882dc46c1b6b79ce8cf1070a05bdaa/assets/icon/user/vip_lv2.webp
--------------------------------------------------------------------------------
/assets/icon/user/vip_lv3.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fluttercandies/flutter_juejin/09a5c2de8b882dc46c1b6b79ce8cf1070a05bdaa/assets/icon/user/vip_lv3.webp
--------------------------------------------------------------------------------
/assets/icon/user/vip_lv4.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fluttercandies/flutter_juejin/09a5c2de8b882dc46c1b6b79ce8cf1070a05bdaa/assets/icon/user/vip_lv4.webp
--------------------------------------------------------------------------------
/assets/icon/user/vip_lv5.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fluttercandies/flutter_juejin/09a5c2de8b882dc46c1b6b79ce8cf1070a05bdaa/assets/icon/user/vip_lv5.webp
--------------------------------------------------------------------------------
/build.yaml:
--------------------------------------------------------------------------------
1 | targets:
2 | $default:
3 | builders:
4 | json_serializable:
5 | options:
6 | field_rename: snake
7 | explicit_to_json: true
8 |
--------------------------------------------------------------------------------
/ff_annotation_route_commands:
--------------------------------------------------------------------------------
1 | --output=routes --routes-file-output=routes --super-arguments --save
--------------------------------------------------------------------------------
/ios/.gitignore:
--------------------------------------------------------------------------------
1 | **/dgph
2 | *.mode1v3
3 | *.mode2v3
4 | *.moved-aside
5 | *.pbxuser
6 | *.perspectivev3
7 | **/*sync/
8 | .sconsign.dblite
9 | .tags*
10 | **/.vagrant/
11 | **/DerivedData/
12 | Icon?
13 | **/Pods/
14 | **/.symlinks/
15 | Podfile.lock
16 | profile
17 | xcuserdata
18 | **/.generated/
19 | Flutter/App.framework
20 | Flutter/Flutter.framework
21 | Flutter/Flutter.podspec
22 | Flutter/Generated.xcconfig
23 | Flutter/ephemeral/
24 | Flutter/app.flx
25 | Flutter/app.zip
26 | Flutter/flutter_assets/
27 | Flutter/flutter_export_environment.sh
28 | ServiceDefinitions.json
29 | Runner/GeneratedPluginRegistrant.*
30 |
31 | # Exceptions to above rules.
32 | !default.mode1v3
33 | !default.mode2v3
34 | !default.pbxuser
35 | !default.perspectivev3
36 |
--------------------------------------------------------------------------------
/ios/Flutter/AppFrameworkInfo.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | App
9 | CFBundleIdentifier
10 | io.flutter.flutter.app
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | App
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1.0
23 | MinimumOSVersion
24 | 9.0
25 |
26 |
27 |
--------------------------------------------------------------------------------
/ios/Flutter/Debug.xcconfig:
--------------------------------------------------------------------------------
1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
2 | #include "Generated.xcconfig"
3 |
--------------------------------------------------------------------------------
/ios/Flutter/Release.xcconfig:
--------------------------------------------------------------------------------
1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
2 | #include "Generated.xcconfig"
3 |
--------------------------------------------------------------------------------
/ios/Podfile:
--------------------------------------------------------------------------------
1 | # Uncomment this line to define a global platform for your project
2 | platform :ios, '9.0'
3 |
4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency.
5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true'
6 |
7 | project 'Runner', {
8 | 'Debug' => :debug,
9 | 'Profile' => :release,
10 | 'Release' => :release,
11 | }
12 |
13 | def flutter_root
14 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
15 | unless File.exist?(generated_xcode_build_settings_path)
16 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
17 | end
18 |
19 | File.foreach(generated_xcode_build_settings_path) do |line|
20 | matches = line.match(/FLUTTER_ROOT\=(.*)/)
21 | return matches[1].strip if matches
22 | end
23 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
24 | end
25 |
26 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
27 |
28 | flutter_ios_podfile_setup
29 |
30 | def install_plugin_pods(application_path = nil, relative_symlink_dir, platform)
31 | # defined_in_file is set by CocoaPods and is a Pathname to the Podfile.
32 | application_path ||= File.dirname(defined_in_file.realpath) if self.respond_to?(:defined_in_file)
33 | raise 'Could not find application path' unless application_path
34 |
35 | # Prepare symlinks folder. We use symlinks to avoid having Podfile.lock
36 | # referring to absolute paths on developers' machines.
37 |
38 | symlink_dir = File.expand_path(relative_symlink_dir, application_path)
39 | system('rm', '-rf', symlink_dir) # Avoid the complication of dependencies like FileUtils.
40 |
41 | symlink_plugins_dir = File.expand_path('plugins', symlink_dir)
42 | system('mkdir', '-p', symlink_plugins_dir)
43 |
44 | plugins_file = File.join(application_path, '..', '.flutter-plugins-dependencies')
45 | plugin_pods = flutter_parse_plugins_file(plugins_file, platform)
46 | plugin_pods.each do |plugin_hash|
47 | plugin_name = plugin_hash['name']
48 | plugin_path = plugin_hash['path']
49 | has_native_build = plugin_hash.fetch('native_build', true)
50 | if (plugin_name && plugin_path && has_native_build)
51 | specPath = "#{plugin_path}/#{platform}/#{plugin_name}.podspec"
52 | pod plugin_name, :path => specPath
53 | end
54 | end
55 | end
56 |
57 | target 'Runner' do
58 | use_frameworks!
59 | use_modular_headers!
60 |
61 | flutter_install_ios_engine_pod(File.dirname(File.realpath(__FILE__)))
62 | install_plugin_pods(File.dirname(File.realpath(__FILE__)), '.symlinks', 'ios')
63 | end
64 |
65 | post_install do |installer|
66 | installer.pods_project.targets.each do |target|
67 | flutter_additional_ios_build_settings(target)
68 | end
69 | end
70 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreviewsEnabled
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
37 |
38 |
39 |
40 |
41 |
42 |
52 |
54 |
60 |
61 |
62 |
63 |
69 |
71 |
77 |
78 |
79 |
80 |
82 |
83 |
86 |
87 |
88 |
--------------------------------------------------------------------------------
/ios/Runner.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreviewsEnabled
6 |
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: [UIApplication.LaunchOptionsKey: 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-20@2x.png",
7 | "scale": "2x"
8 | },
9 | {
10 | "size": "20x20",
11 | "idiom": "iphone",
12 | "filename": "icon-20@3x.png",
13 | "scale": "3x"
14 | },
15 | {
16 | "size": "29x29",
17 | "idiom": "iphone",
18 | "filename": "icon-29.png",
19 | "scale": "1x"
20 | },
21 | {
22 | "size": "29x29",
23 | "idiom": "iphone",
24 | "filename": "icon-29@2x.png",
25 | "scale": "2x"
26 | },
27 | {
28 | "size": "29x29",
29 | "idiom": "iphone",
30 | "filename": "icon-29@3x.png",
31 | "scale": "3x"
32 | },
33 | {
34 | "size": "40x40",
35 | "idiom": "iphone",
36 | "filename": "icon-40@2x.png",
37 | "scale": "2x"
38 | },
39 | {
40 | "size": "40x40",
41 | "idiom": "iphone",
42 | "filename": "icon-40@3x.png",
43 | "scale": "3x"
44 | },
45 | {
46 | "size": "60x60",
47 | "idiom": "iphone",
48 | "filename": "icon-60@2x.png",
49 | "scale": "2x"
50 | },
51 | {
52 | "size": "60x60",
53 | "idiom": "iphone",
54 | "filename": "icon-60@3x.png",
55 | "scale": "3x"
56 | },
57 | {
58 | "size": "20x20",
59 | "idiom": "ipad",
60 | "filename": "icon-20-ipad.png",
61 | "scale": "1x"
62 | },
63 | {
64 | "size": "20x20",
65 | "idiom": "ipad",
66 | "filename": "icon-20@2x-ipad.png",
67 | "scale": "2x"
68 | },
69 | {
70 | "size": "29x29",
71 | "idiom": "ipad",
72 | "filename": "icon-29-ipad.png",
73 | "scale": "1x"
74 | },
75 | {
76 | "size": "29x29",
77 | "idiom": "ipad",
78 | "filename": "icon-29@2x-ipad.png",
79 | "scale": "2x"
80 | },
81 | {
82 | "size": "40x40",
83 | "idiom": "ipad",
84 | "filename": "icon-40.png",
85 | "scale": "1x"
86 | },
87 | {
88 | "size": "40x40",
89 | "idiom": "ipad",
90 | "filename": "icon-40@2x.png",
91 | "scale": "2x"
92 | },
93 | {
94 | "size": "76x76",
95 | "idiom": "ipad",
96 | "filename": "icon-76.png",
97 | "scale": "1x"
98 | },
99 | {
100 | "size": "76x76",
101 | "idiom": "ipad",
102 | "filename": "icon-76@2x.png",
103 | "scale": "2x"
104 | },
105 | {
106 | "size": "83.5x83.5",
107 | "idiom": "ipad",
108 | "filename": "icon-83.5@2x.png",
109 | "scale": "2x"
110 | },
111 | {
112 | "size": "1024x1024",
113 | "idiom": "ios-marketing",
114 | "filename": "icon-1024.png",
115 | "scale": "1x"
116 | }
117 | ],
118 | "info": {
119 | "version": 1,
120 | "author": "icon.wuruihong.com"
121 | }
122 | }
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-1024.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fluttercandies/flutter_juejin/09a5c2de8b882dc46c1b6b79ce8cf1070a05bdaa/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-1024.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-20-ipad.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fluttercandies/flutter_juejin/09a5c2de8b882dc46c1b6b79ce8cf1070a05bdaa/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-20-ipad.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-20@2x-ipad.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fluttercandies/flutter_juejin/09a5c2de8b882dc46c1b6b79ce8cf1070a05bdaa/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-20@2x-ipad.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-20@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fluttercandies/flutter_juejin/09a5c2de8b882dc46c1b6b79ce8cf1070a05bdaa/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-20@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-20@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fluttercandies/flutter_juejin/09a5c2de8b882dc46c1b6b79ce8cf1070a05bdaa/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-20@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-29-ipad.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fluttercandies/flutter_juejin/09a5c2de8b882dc46c1b6b79ce8cf1070a05bdaa/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-29-ipad.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-29.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fluttercandies/flutter_juejin/09a5c2de8b882dc46c1b6b79ce8cf1070a05bdaa/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-29.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-29@2x-ipad.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fluttercandies/flutter_juejin/09a5c2de8b882dc46c1b6b79ce8cf1070a05bdaa/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-29@2x-ipad.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-29@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fluttercandies/flutter_juejin/09a5c2de8b882dc46c1b6b79ce8cf1070a05bdaa/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-29@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-29@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fluttercandies/flutter_juejin/09a5c2de8b882dc46c1b6b79ce8cf1070a05bdaa/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-29@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-40.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fluttercandies/flutter_juejin/09a5c2de8b882dc46c1b6b79ce8cf1070a05bdaa/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-40.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-40@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fluttercandies/flutter_juejin/09a5c2de8b882dc46c1b6b79ce8cf1070a05bdaa/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-40@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-40@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fluttercandies/flutter_juejin/09a5c2de8b882dc46c1b6b79ce8cf1070a05bdaa/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-40@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-60@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fluttercandies/flutter_juejin/09a5c2de8b882dc46c1b6b79ce8cf1070a05bdaa/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-60@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-60@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fluttercandies/flutter_juejin/09a5c2de8b882dc46c1b6b79ce8cf1070a05bdaa/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-60@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-76.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fluttercandies/flutter_juejin/09a5c2de8b882dc46c1b6b79ce8cf1070a05bdaa/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-76.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-76@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fluttercandies/flutter_juejin/09a5c2de8b882dc46c1b6b79ce8cf1070a05bdaa/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-76@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-83.5@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fluttercandies/flutter_juejin/09a5c2de8b882dc46c1b6b79ce8cf1070a05bdaa/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-83.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/fluttercandies/flutter_juejin/09a5c2de8b882dc46c1b6b79ce8cf1070a05bdaa/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fluttercandies/flutter_juejin/09a5c2de8b882dc46c1b6b79ce8cf1070a05bdaa/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fluttercandies/flutter_juejin/09a5c2de8b882dc46c1b6b79ce8cf1070a05bdaa/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 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleDisplayName
8 | Juejin
9 | CFBundleExecutable
10 | $(EXECUTABLE_NAME)
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | juejin
17 | CFBundlePackageType
18 | APPL
19 | CFBundleShortVersionString
20 | $(FLUTTER_BUILD_NAME)
21 | CFBundleSignature
22 | ????
23 | CFBundleVersion
24 | $(FLUTTER_BUILD_NUMBER)
25 | LSRequiresIPhoneOS
26 |
27 | UILaunchStoryboardName
28 | LaunchScreen
29 | UIMainStoryboardFile
30 | Main
31 | UISupportedInterfaceOrientations
32 |
33 | UIInterfaceOrientationPortrait
34 | UIInterfaceOrientationLandscapeLeft
35 | UIInterfaceOrientationLandscapeRight
36 |
37 | UISupportedInterfaceOrientations~ipad
38 |
39 | UIInterfaceOrientationPortrait
40 | UIInterfaceOrientationPortraitUpsideDown
41 | UIInterfaceOrientationLandscapeLeft
42 | UIInterfaceOrientationLandscapeRight
43 |
44 | UIViewControllerBasedStatusBarAppearance
45 |
46 | CADisableMinimumFrameDurationOnPhone
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/ios/Runner/Runner-Bridging-Header.h:
--------------------------------------------------------------------------------
1 | #import "GeneratedPluginRegistrant.h"
2 |
--------------------------------------------------------------------------------
/l10n.yaml:
--------------------------------------------------------------------------------
1 | arb-dir: lib/l10n
2 | output-class: JJLocalizations
3 | output-dir: lib/l10n/gen
4 | output-localization-file: jj_localizations.dart
5 | template-arb-file: app_en.arb
6 | synthetic-package: false
7 | untranslated-messages-file: lib/l10n/gen/jj_localizations_untranslated.txt
8 |
--------------------------------------------------------------------------------
/lib/apis/content_api.dart:
--------------------------------------------------------------------------------
1 | // Copyright 2022 The FlutterCandies author. All rights reserved.
2 | // Use of this source code is governed by a MIT license that can be found in the
3 | // LICENSE file.
4 |
5 | import '../internals/urls.dart';
6 | import '../models/data_model.dart';
7 | import '../models/response_model.dart';
8 | import '../utils/http_util.dart';
9 |
10 | class ContentAPI {
11 | const ContentAPI._();
12 |
13 | static const String _api = '${Urls.apiHost}/content_api/v1';
14 | static const String _articles = '$_api/article';
15 |
16 | static Future> getDetailById(String id) {
17 | return HttpUtil.fetchModel(
18 | FetchType.post,
19 | url: '$_articles/detail',
20 | body: {'article_id': id},
21 | );
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/lib/apis/interact_api.dart:
--------------------------------------------------------------------------------
1 | // Copyright 2022 The FlutterCandies author. All rights reserved.
2 | // Use of this source code is governed by a MIT license that can be found in the
3 | // LICENSE file.
4 |
5 | import '../internals/urls.dart';
6 | import '../models/data_model.dart';
7 | import '../models/response_model.dart';
8 | import '../utils/http_util.dart';
9 |
10 | class InteractAPI {
11 | const InteractAPI._();
12 |
13 | static const String _api = '${Urls.apiHost}/interact_api/v1';
14 | static const String _comment = '$_api/comment';
15 |
16 | static Future> getCommentByTypeAndId({
17 | required FeedItemType type,
18 | required String id,
19 | String? lastId,
20 | int limit = 20,
21 | }) {
22 | return HttpUtil.fetchModel(
23 | FetchType.post,
24 | url: '$_comment/list',
25 | body: {
26 | 'item_type': type.type,
27 | 'item_id': id,
28 | 'cursor': lastId,
29 | 'limit': limit,
30 | },
31 | );
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/lib/apis/passport_api.dart:
--------------------------------------------------------------------------------
1 | // Copyright 2022 The FlutterCandies author. All rights reserved.
2 | // Use of this source code is governed by a MIT license that can be found in the
3 | // LICENSE file.
4 |
5 | import 'package:dio/dio.dart';
6 |
7 | import '../constants/constants.dart';
8 | import '../extensions/string_extension.dart';
9 | import '../internals/urls.dart';
10 | import '../models/data_model.dart';
11 | import '../models/response_model.dart';
12 | import '../utils/http_util.dart';
13 |
14 | class PassportAPI {
15 | const PassportAPI._();
16 |
17 | static const String _api = '${Urls.apiHost}/passport';
18 | static const String _user = '$_api/user';
19 |
20 | static Future> login(String u, String p) {
21 | return HttpUtil.fetchModel(
22 | FetchType.post,
23 | url: '$_user/login/',
24 | queryParameters: {
25 | 'aid': '$appId',
26 | 'iid': '2397406981526599', // TODO: 确认构造来源
27 | 'mix_mode': '1',
28 | },
29 | body: {
30 | 'account': u.toEncrypted,
31 | 'password': p.toEncrypted,
32 | },
33 | contentType: Headers.formUrlEncodedContentType,
34 | );
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/lib/apis/tag_api.dart:
--------------------------------------------------------------------------------
1 | // Copyright 2022 The FlutterCandies author. All rights reserved.
2 | // Use of this source code is governed by a MIT license that can be found in the
3 | // LICENSE file.
4 |
5 | import '../internals/urls.dart';
6 | import '../models/data_model.dart';
7 | import '../models/response_model.dart';
8 | import '../utils/http_util.dart';
9 |
10 | class TagAPI {
11 | const TagAPI._();
12 |
13 | static const String _api = '${Urls.apiHost}/tag_api/v1';
14 |
15 | static Future> getCategoryBriefs() {
16 | return HttpUtil.fetchModel(
17 | FetchType.get,
18 | url: '$_api/query_category_briefs',
19 | );
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/lib/constants/constants.dart:
--------------------------------------------------------------------------------
1 | // Copyright 2022 The FlutterCandies author. All rights reserved.
2 | // Use of this source code is governed by a MIT license that can be found in the
3 | // LICENSE file.
4 |
5 | import 'dart:convert';
6 | import 'dart:typed_data';
7 |
8 | const int appId = 2606;
9 |
10 | const JsonEncoder globalJsonEncoder = JsonEncoder.withIndent(' ');
11 |
12 | const String urlScheme = r'http(s?)://';
13 | final RegExp urlRegExp = RegExp(urlScheme);
14 |
15 | // transparent image 2:1
16 | final kTransparentImage = Uint8List.fromList([
17 | 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, //
18 | 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, //
19 | 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, //
20 | 0x01, 0x03, 0x00, 0x00, 0x00, 0xce, 0xec, 0xed, //
21 | 0xc9, 0x00, 0x00, 0x00, 0x06, 0x50, 0x4c, 0x54, //
22 | 0x45, 0x00, 0x00, 0x00, 0xc9, 0x45, 0x26, 0x64, //
23 | 0x1a, 0x2a, 0xbd, 0x00, 0x00, 0x00, 0x02, 0x74,
24 | 0x52, 0x4e, 0x53, 0xff, 0x00, 0xe5, 0xb7, 0x30,
25 | 0x4a, 0x00, 0x00, 0x00, 0x09, 0x70, 0x48, 0x59,
26 | 0x73, 0x00, 0x00, 0x0b, 0x13, 0x00, 0x00, 0x0b,
27 | 0x13, 0x01, 0x00, 0x9a, 0x9c, 0x18, 0x00, 0x00,
28 | 0x00, 0x0a, 0x49, 0x44, 0x41, 0x54, 0x08, 0x99,
29 | 0x63, 0x38, 0x00, 0x00, 0x00, 0xc2, 0x00, 0xc1,
30 | 0xee, 0x80, 0x97, 0x86, 0x00, 0x00, 0x00, 0x00,
31 | 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82,
32 | ]);
33 |
--------------------------------------------------------------------------------
/lib/constants/resources.dart:
--------------------------------------------------------------------------------
1 | /// Generate by [resource_generator](https://github.com/CaiJingLong/flutter_resource_generator) library.
2 | /// PLEASE DO NOT EDIT MANUALLY.
3 | // ignore_for_file: constant_identifier_names
4 | class R {
5 | const R._();
6 |
7 | static const String ASSETS_BRAND_WITH_TEXT_SVG = 'assets/brand-with-text.svg';
8 |
9 | static const String ASSETS_BRAND_SVG = 'assets/brand.svg';
10 |
11 | static const String ASSETS_ICON_POST_HOT_COMMENT_PNG =
12 | 'assets/icon/post/hot_comment.png';
13 |
14 | static const String ASSETS_ICON_USER_JY_LV1_WEBP =
15 | 'assets/icon/user/jy_lv1.webp';
16 |
17 | static const String ASSETS_ICON_USER_JY_LV2_WEBP =
18 | 'assets/icon/user/jy_lv2.webp';
19 |
20 | static const String ASSETS_ICON_USER_JY_LV3_WEBP =
21 | 'assets/icon/user/jy_lv3.webp';
22 |
23 | static const String ASSETS_ICON_USER_JY_LV4_WEBP =
24 | 'assets/icon/user/jy_lv4.webp';
25 |
26 | static const String ASSETS_ICON_USER_JY_LV5_WEBP =
27 | 'assets/icon/user/jy_lv5.webp';
28 |
29 | static const String ASSETS_ICON_USER_JY_LV6_WEBP =
30 | 'assets/icon/user/jy_lv6.webp';
31 |
32 | static const String ASSETS_ICON_USER_JY_LV7_WEBP =
33 | 'assets/icon/user/jy_lv7.webp';
34 |
35 | static const String ASSETS_ICON_USER_JY_LV8_WEBP =
36 | 'assets/icon/user/jy_lv8.webp';
37 |
38 | static const String ASSETS_ICON_USER_LV1_WEBP = 'assets/icon/user/lv1.webp';
39 |
40 | static const String ASSETS_ICON_USER_LV2_WEBP = 'assets/icon/user/lv2.webp';
41 |
42 | static const String ASSETS_ICON_USER_LV3_WEBP = 'assets/icon/user/lv3.webp';
43 |
44 | static const String ASSETS_ICON_USER_LV4_WEBP = 'assets/icon/user/lv4.webp';
45 |
46 | static const String ASSETS_ICON_USER_LV5_WEBP = 'assets/icon/user/lv5.webp';
47 |
48 | static const String ASSETS_ICON_USER_LV6_WEBP = 'assets/icon/user/lv6.webp';
49 |
50 | static const String ASSETS_ICON_USER_LV7_WEBP = 'assets/icon/user/lv7.webp';
51 |
52 | static const String ASSETS_ICON_USER_LV8_WEBP = 'assets/icon/user/lv8.webp';
53 |
54 | static const String ASSETS_ICON_USER_VIP_LV1_WEBP =
55 | 'assets/icon/user/vip_lv1.webp';
56 |
57 | static const String ASSETS_ICON_USER_VIP_LV2_WEBP =
58 | 'assets/icon/user/vip_lv2.webp';
59 |
60 | static const String ASSETS_ICON_USER_VIP_LV3_WEBP =
61 | 'assets/icon/user/vip_lv3.webp';
62 |
63 | static const String ASSETS_ICON_USER_VIP_LV4_WEBP =
64 | 'assets/icon/user/vip_lv4.webp';
65 |
66 | static const String ASSETS_ICON_USER_VIP_LV5_WEBP =
67 | 'assets/icon/user/vip_lv5.webp';
68 |
69 | static const String PIN_DETAIL_HEAD_BG_WEBP =
70 | 'assets/icon/topic/pin_detail_head_bg.webp';
71 | }
72 |
--------------------------------------------------------------------------------
/lib/constants/screens.dart:
--------------------------------------------------------------------------------
1 | // Copyright 2022 The FlutterCandies author. All rights reserved.
2 | // Use of this source code is governed by a MIT license that can be found in the
3 | // LICENSE file.
4 |
5 | import 'dart:ui';
6 |
7 | import 'package:flutter/material.dart';
8 | import 'package:flutter/services.dart';
9 |
10 | class Screens {
11 | const Screens._();
12 |
13 | static MediaQueryData get mediaQuery => MediaQueryData.fromView(
14 | WidgetsBinding.instance.platformDispatcher.views.first,
15 | );
16 |
17 | static double fixedFontSize(double fontSize) => fontSize / textScaleFactor;
18 |
19 | static double get scale => mediaQuery.devicePixelRatio;
20 |
21 | static double get width => mediaQuery.size.width;
22 |
23 | static int get widthPixels => (width * scale).toInt();
24 |
25 | static double get height => mediaQuery.size.height;
26 |
27 | static int get heightPixels => (height * scale).toInt();
28 |
29 | static double get aspectRatio => width / height;
30 |
31 | static double get textScaleFactor => mediaQuery.textScaleFactor;
32 |
33 | static double get navigationBarHeight =>
34 | mediaQuery.padding.top + kToolbarHeight;
35 |
36 | static double get topSafeHeight => mediaQuery.padding.top;
37 |
38 | static double get bottomSafeHeight => mediaQuery.padding.bottom;
39 |
40 | static double get safeHeight => height - topSafeHeight - bottomSafeHeight;
41 |
42 | static FlutterView get window =>
43 | WidgetsBinding.instance.platformDispatcher.views.first;
44 |
45 | static void updateStatusBarStyle(SystemUiOverlayStyle style) {
46 | SystemChrome.setSystemUIOverlayStyle(style);
47 | }
48 | }
49 |
50 | extension ScreenSizeDoubleExtension on double {
51 | int toPx() => (this * Screens.scale).toInt();
52 | }
53 |
--------------------------------------------------------------------------------
/lib/constants/styles.dart:
--------------------------------------------------------------------------------
1 | // Copyright 2022 The FlutterCandies author. All rights reserved.
2 | // Use of this source code is governed by a MIT license that can be found in the
3 | // LICENSE file.
4 |
5 | import 'package:flutter/material.dart';
6 |
7 | const Color failedColor = Color(0xffff6363);
8 |
9 | class RadiusConstants {
10 | const RadiusConstants._();
11 |
12 | static const BorderRadius r1 = BorderRadius.all(Radius.circular(1));
13 | static const BorderRadius r2 = BorderRadius.all(Radius.circular(2));
14 | static const BorderRadius r3 = BorderRadius.all(Radius.circular(3));
15 | static const BorderRadius r4 = BorderRadius.all(Radius.circular(4));
16 | static const BorderRadius r5 = BorderRadius.all(Radius.circular(5));
17 | static const BorderRadius r6 = BorderRadius.all(Radius.circular(6));
18 | static const BorderRadius r7 = BorderRadius.all(Radius.circular(7));
19 | static const BorderRadius r8 = BorderRadius.all(Radius.circular(8));
20 | static const BorderRadius r9 = BorderRadius.all(Radius.circular(9));
21 | static const BorderRadius r10 = BorderRadius.all(Radius.circular(10));
22 | static const BorderRadius r12 = BorderRadius.all(Radius.circular(12));
23 | static const BorderRadius r15 = BorderRadius.all(Radius.circular(15));
24 | static const BorderRadius r16 = BorderRadius.all(Radius.circular(16));
25 | static const BorderRadius r20 = BorderRadius.all(Radius.circular(20));
26 | static const BorderRadius r25 = BorderRadius.all(Radius.circular(25));
27 | static const BorderRadius max = BorderRadius.all(Radius.circular(999999));
28 | }
29 |
--------------------------------------------------------------------------------
/lib/exports.dart:
--------------------------------------------------------------------------------
1 | export 'package:collection/collection.dart';
2 | export 'package:dio/dio.dart';
3 | export 'package:ff_annotation_route_library/ff_annotation_route_library.dart';
4 | export 'package:flutter/services.dart' show HapticFeedback, TextInputFormatter;
5 | export 'package:flutter_svg/flutter_svg.dart' show SvgPicture;
6 | export 'package:pull_to_refresh_notification/pull_to_refresh_notification.dart';
7 |
8 | export 'apis/content_api.dart';
9 | export 'apis/interact_api.dart';
10 | export 'apis/passport_api.dart';
11 | export 'apis/recommend_api.dart';
12 | export 'apis/tag_api.dart';
13 | export 'constants/constants.dart';
14 | export 'constants/resources.dart';
15 | export 'constants/screens.dart';
16 | export 'constants/styles.dart';
17 | export 'constants/themes.dart';
18 | export 'extensions/brightness_extension.dart';
19 | export 'extensions/build_context_extension.dart';
20 | export 'extensions/color_extension.dart';
21 | export 'extensions/duration_extension.dart';
22 | export 'extensions/future_extension.dart';
23 | export 'extensions/num_extension.dart';
24 | export 'extensions/state_extension.dart';
25 | export 'extensions/string_extension.dart';
26 | export 'internals/instance.dart';
27 | export 'internals/methods.dart';
28 | export 'internals/special_text.dart';
29 | export 'internals/urls.dart';
30 | export 'l10n/gen/jj_localizations.dart';
31 | export 'models/data_model.dart';
32 | export 'models/loading_base.dart';
33 | export 'models/response_model.dart';
34 | export 'routes/juejin_routes.dart';
35 | export 'routes/page_route.dart';
36 | export 'utils/cache_util.dart';
37 | export 'utils/device_util.dart';
38 | export 'utils/haptic_util.dart';
39 | export 'utils/http_util.dart';
40 | export 'utils/log_util.dart';
41 | export 'utils/package_util.dart';
42 | export 'utils/toast_util.dart';
43 | export 'widgets/_none.dart';
44 | export 'widgets/error_widget.dart';
45 | export 'widgets/gaps.dart';
46 | export 'widgets/lazy_indexed_stack.dart';
47 | export 'widgets/logo.dart';
48 | export 'widgets/refresh/base_refresh_wrapper.dart';
49 | export 'widgets/refresh/refresh_grid_wrapper.dart';
50 | export 'widgets/refresh/refresh_list_wrapper.dart';
51 | export 'widgets/tapper.dart';
52 | export 'widgets/webview.dart';
53 |
--------------------------------------------------------------------------------
/lib/extensions/brightness_extension.dart:
--------------------------------------------------------------------------------
1 | // Copyright 2022 The FlutterCandies author. All rights reserved.
2 | // Use of this source code is governed by a MIT license that can be found in the
3 | // LICENSE file.
4 |
5 | import 'dart:ui';
6 |
7 | extension BrightnessExtension on Brightness {
8 | bool get isDark => this == Brightness.dark;
9 |
10 | bool get isLight => this == Brightness.light;
11 | }
12 |
--------------------------------------------------------------------------------
/lib/extensions/build_context_extension.dart:
--------------------------------------------------------------------------------
1 | // Copyright 2022 The FlutterCandies author. All rights reserved.
2 | // Use of this source code is governed by a MIT license that can be found in the
3 | // LICENSE file.
4 |
5 | import 'package:flutter/material.dart';
6 |
7 | import '../l10n/gen/jj_localizations.dart';
8 |
9 | extension BuildContextExtension on BuildContext {
10 | JJLocalizations get l10n => JJLocalizations.of(this)!;
11 |
12 | NavigatorState get navigator => Navigator.of(this);
13 |
14 | ThemeData get theme => Theme.of(this);
15 |
16 | TextTheme get textTheme => theme.textTheme;
17 |
18 | Brightness get brightness => theme.brightness;
19 |
20 | ButtonThemeData get buttonTheme => ButtonTheme.of(this);
21 |
22 | IconThemeData get iconTheme => IconTheme.of(this);
23 |
24 | MediaQueryData get mediaQuery => MediaQuery.of(this);
25 |
26 | double get topPadding => mediaQuery.padding.top;
27 |
28 | double get bottomPadding => mediaQuery.padding.bottom;
29 |
30 | double get bottomViewInsets => mediaQuery.viewInsets.bottom;
31 |
32 | ColorScheme get colorScheme => theme.colorScheme;
33 |
34 | Color get surfaceColor => colorScheme.surface;
35 | }
36 |
--------------------------------------------------------------------------------
/lib/extensions/color_extension.dart:
--------------------------------------------------------------------------------
1 | // Copyright 2022 The FlutterCandies author. All rights reserved.
2 | // Use of this source code is governed by a MIT license that can be found in the
3 | // LICENSE file.
4 |
5 | import 'package:flutter/material.dart';
6 |
7 | extension ColorExtension on Color {
8 | bool get isTransparent => alpha == 0;
9 |
10 | MaterialColor get swatch {
11 | return Colors.primaries.firstWhere(
12 | (Color c) => c.value == value,
13 | orElse: () => MaterialColor(value, getMaterialColorValues),
14 | );
15 | }
16 |
17 | Map get getMaterialColorValues {
18 | return {
19 | 50: _swatchShade(50),
20 | 100: _swatchShade(100),
21 | 200: _swatchShade(200),
22 | 300: _swatchShade(300),
23 | 400: _swatchShade(400),
24 | 500: _swatchShade(500),
25 | 600: _swatchShade(600),
26 | 700: _swatchShade(700),
27 | 800: _swatchShade(800),
28 | 900: _swatchShade(900),
29 | };
30 | }
31 |
32 | Color _swatchShade(int swatchValue) => HSLColor.fromColor(this)
33 | .withLightness(1 - (swatchValue / 1000))
34 | .toColor();
35 | }
36 |
37 | /// https://stackoverflow.com/a/50081214/10064463
38 | extension HexColor on Color {
39 | /// Format: "aabbcc" or "ffaabbcc" with an optional leading "#".
40 | static Color fromHex(String hexString) {
41 | final StringBuffer buffer = StringBuffer();
42 | if (hexString.length == 6 || hexString.length == 7) {
43 | buffer.write('ff');
44 | }
45 | buffer.write(hexString.replaceFirst('#', ''));
46 | return Color(int.parse(buffer.toString(), radix: 16));
47 | }
48 |
49 | /// Prefixes a hash sign if [leadingHashSign] is set to `true`.
50 | String toHex({bool leadingHashSign = true}) => '${leadingHashSign ? '#' : ''}'
51 | '${alpha.toRadixString(16).padLeft(2, '0')}'
52 | '${red.toRadixString(16).padLeft(2, '0')}'
53 | '${green.toRadixString(16).padLeft(2, '0')}'
54 | '${blue.toRadixString(16).padLeft(2, '0')}';
55 | }
56 |
--------------------------------------------------------------------------------
/lib/extensions/duration_extension.dart:
--------------------------------------------------------------------------------
1 | // Copyright 2022 The FlutterCandies author. All rights reserved.
2 | // Use of this source code is governed by a MIT license that can be found in the
3 | // LICENSE file.
4 |
5 | import 'package:flutter/widgets.dart';
6 |
7 | import 'build_context_extension.dart';
8 |
9 | extension DurationExtension on Duration {
10 | Future get delay => Future.delayed(this);
11 |
12 | String differenceString(BuildContext context) {
13 | final localization = context.l10n;
14 | if (this >= const Duration(days: 365)) {
15 | return localization.durationYears(inDays ~/ 365);
16 | }
17 | if (this >= const Duration(days: 30)) {
18 | return localization.durationMonths(inDays ~/ 30);
19 | }
20 | if (this >= const Duration(days: 1)) {
21 | return localization.durationDays(inDays);
22 | }
23 | if (this >= const Duration(hours: 1)) {
24 | return localization.durationHours(inHours);
25 | }
26 | return localization.durationMinutes(inMinutes);
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/lib/extensions/future_extension.dart:
--------------------------------------------------------------------------------
1 | // Copyright 2022 The FlutterCandies author. All rights reserved.
2 | // Use of this source code is governed by a MIT license that can be found in the
3 | // LICENSE file.
4 |
5 | import 'dart:async';
6 |
7 | import '../utils/log_util.dart';
8 |
9 | extension FutureExtension on Future {
10 | Future atLeast(Duration duration) async {
11 | final List futures = await Future.wait(
12 | >[this, Future.delayed(duration)],
13 | );
14 | return futures.first as T;
15 | }
16 |
17 | Future> plus(Future b, {bool eagerError = false}) {
18 | return Future.wait(>[this, b], eagerError: eagerError);
19 | }
20 | }
21 |
22 | extension StopwatchExtension on Stopwatch {
23 | void logElapsed([String? tag]) {
24 | LogUtil.d(
25 | '${tag ?? 'Stopwatch'} elapsed: $elapsed',
26 | tag: '⌚ Stopwatch',
27 | );
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/lib/extensions/num_extension.dart:
--------------------------------------------------------------------------------
1 | // Copyright 2022 The FlutterCandies author. All rights reserved.
2 | // Use of this source code is governed by a MIT license that can be found in the
3 | // LICENSE file.
4 |
5 | import 'dart:math' as math;
6 |
7 | final math.Random _random = math.Random();
8 |
9 | extension NumExtension on T {
10 | T get lessThanOne => math.min((this is int ? 1 : 1.0) as T, this);
11 |
12 | T get lessThanZero => math.min((this is int ? 0 : 0.0) as T, this);
13 |
14 | T get moreThanOne => math.max((this is int ? 1 : 1.0) as T, this);
15 |
16 | T get moreThanZero => math.max((this is int ? 0 : 0.0) as T, this);
17 |
18 | T get betweenZeroAndOne => lessThanOne.moreThanZero;
19 | }
20 |
21 | extension IntExtension on int {
22 | String get fileSizeFromBytes {
23 | const int kb = 1024;
24 | const int mb = 1024 * kb;
25 | const int gb = 1024 * mb;
26 | if (this >= gb) {
27 | return '${(this / gb).toStringAsFixed(2)} GB';
28 | }
29 | if (this >= mb) {
30 | return '${(this / mb).toStringAsFixed(2)} MB';
31 | }
32 | if (this >= kb) {
33 | return '${(this / kb).toStringAsFixed(2)} KB';
34 | }
35 | return '$this B';
36 | }
37 |
38 | int nextRandom([int min = 0]) => min + _random.nextInt(this - min);
39 |
40 | Duration get days => Duration(days: this);
41 |
42 | Duration get hours => Duration(hours: this);
43 |
44 | Duration get minutes => Duration(minutes: this);
45 |
46 | Duration get seconds => Duration(seconds: this);
47 |
48 | Duration get milliseconds => Duration(milliseconds: this);
49 |
50 | Duration get microseconds => Duration(microseconds: this);
51 |
52 | DateTime get toDateTimeInMicroseconds =>
53 | DateTime.fromMicrosecondsSinceEpoch(this);
54 |
55 | DateTime get toDateTimeInMilliseconds =>
56 | DateTime.fromMillisecondsSinceEpoch(this);
57 | }
58 |
59 | extension DoubleExtension on double {
60 | double nextRandom([int min = 0]) => min + _random.nextDouble() * (this - min);
61 |
62 | /// 根据位数四舍五入
63 | double roundAsFixed(int size) {
64 | final num pow = math.pow(10, size);
65 | return (this * pow).round() / pow;
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/lib/extensions/state_extension.dart:
--------------------------------------------------------------------------------
1 | // Copyright 2022 The FlutterCandies author. All rights reserved.
2 | // Use of this source code is governed by a MIT license that can be found in the
3 | // LICENSE file.
4 |
5 | import 'dart:async';
6 |
7 | import 'package:flutter/widgets.dart';
8 |
9 | extension SafeSetStateExtension on State {
10 | /// [setState] when it's not building, then wait until next frame built.
11 | FutureOr safeSetState(FutureOr Function() fn) async {
12 | await fn();
13 | if (mounted &&
14 | !context.debugDoingBuild &&
15 | context.owner?.debugBuilding == false) {
16 | // ignore: invalid_use_of_protected_member
17 | setState(() {});
18 | }
19 | final Completer completer = Completer();
20 | WidgetsBinding.instance.addPostFrameCallback((_) {
21 | completer.complete();
22 | });
23 | return completer.future;
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/lib/extensions/string_extension.dart:
--------------------------------------------------------------------------------
1 | // Copyright 2022 The FlutterCandies author. All rights reserved.
2 | // Use of this source code is governed by a MIT license that can be found in the
3 | // LICENSE file.
4 |
5 | import 'package:flutter/widgets.dart' show Characters;
6 |
7 | extension StringExtension on String {
8 | String get notBreak => Characters(this).join('\u{200B}');
9 |
10 | int toInt() => int.parse(this);
11 |
12 | double toDouble() => double.parse(this);
13 |
14 | String removeAll(Pattern pattern) => replaceAll(pattern, '');
15 |
16 | String removeFirst(Pattern pattern, [int startIndex = 0]) =>
17 | replaceFirst(pattern, '', startIndex);
18 | }
19 |
20 | extension NullableStringExtension on String? {
21 | bool get isNullOrEmpty => this == null || this!.isEmpty;
22 |
23 | bool get isNotNullOrEmpty => this != null && this!.isNotEmpty;
24 |
25 | int? toIntOrNull() => this == null ? null : int.tryParse(this!);
26 |
27 | double? toDoubleOrNull() => this == null ? null : double.tryParse(this!);
28 | }
29 |
30 | /// Try with the DartPad:
31 | /// https://dartpad.cn/?id=98317a28d0cc65f63454c0329f5316ba
32 | ///
33 | /// Thanks to @DemoJameson .
34 | extension EncryptStringExtension on String {
35 | String get toEncrypted {
36 | return split('')
37 | .map((String e) {
38 | final int ascii = e.codeUnitAt(0);
39 | final int r = ascii % 8;
40 | final int offset =
41 | {2: 7, 3: 6, 0: 5, 1: 4, 6: 3, 7: 2, 4: 1, 5: 0}[r]!;
42 | return (ascii + offset - r).toRadixString(16);
43 | })
44 | .join()
45 | .toLowerCase();
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/lib/internals/instance.dart:
--------------------------------------------------------------------------------
1 | // Copyright 2022 The FlutterCandies author. All rights reserved.
2 | // Use of this source code is governed by a MIT license that can be found in the
3 | // LICENSE file.
4 |
5 | import 'package:flutter/material.dart';
6 |
7 | import '../app.dart';
8 |
9 | class JJ {
10 | const JJ._();
11 |
12 | static final GlobalKey navigatorKey = GlobalKey();
13 | static final RouteObserver> routeObserver =
14 | RouteObserver>();
15 | static final GlobalKey repaintBoundaryKey = GlobalKey();
16 | static final GlobalKey appKey = GlobalKey();
17 | }
18 |
19 | NavigatorState get navigator => JJ.navigatorKey.currentState!;
20 |
21 | BuildContext get overlayContext => navigator.overlay!.context;
22 |
23 | DateTime get currentTime => DateTime.now();
24 |
25 | int get currentTimeStamp => currentTime.millisecondsSinceEpoch;
26 |
--------------------------------------------------------------------------------
/lib/internals/methods.dart:
--------------------------------------------------------------------------------
1 | // Copyright 2022 The FlutterCandies author. All rights reserved.
2 | // Use of this source code is governed by a MIT license that can be found in the
3 | // LICENSE file.
4 |
5 | import 'dart:async';
6 | import 'dart:convert';
7 | import 'dart:io';
8 |
9 | import 'package:flutter/material.dart';
10 | import 'package:flutter/services.dart';
11 |
12 | import '../utils/log_util.dart';
13 | // import 'package:permission_handler/permission_handler.dart';
14 |
15 | /// Check permissions and only return whether they succeed or not.
16 | // Future checkPermissions(List permissions) async {
17 | // try {
18 | // final Map status =
19 | // await permissions.request();
20 | // // Delay until next frame.
21 | // await 300.milliseconds.delay;
22 | // return status.values.every((PermissionStatus p) => p.isGranted);
23 | // } catch (e, stack) {
24 | // LogUtil.e('Error when requesting permission: $e', stackTrace: stack);
25 | // return false;
26 | // }
27 | // }
28 |
29 | /// Iterate element and its children to request rebuild.
30 | void rebuildAllChildren(BuildContext context) {
31 | LogUtil.d('Rebuilding all elements...', tag: 'rebuildAllChildren');
32 | void rebuild(Element el) {
33 | el.markNeedsBuild();
34 | el.visitChildren(rebuild);
35 | }
36 |
37 | (context as Element).visitChildren(rebuild);
38 | }
39 |
40 | Map decodeAsJson(String value) {
41 | return jsonDecode(value) as Map;
42 | }
43 |
44 | Future exitApp() async {
45 | await SystemNavigator.pop(animated: true);
46 | exit(0);
47 | }
48 |
--------------------------------------------------------------------------------
/lib/internals/navigator_observer.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | import '../utils/log_util.dart';
4 |
5 | enum RouteAction { pop, push, remove, replace }
6 |
7 | class JJNavigatorObserver extends NavigatorObserver {
8 | static final List> history = >[];
9 |
10 | @override
11 | void didPop(Route route, Route? previousRoute) {
12 | super.didPop(route, previousRoute);
13 | _didRouteChange(RouteAction.pop, previousRoute, route);
14 | history.removeLast();
15 | }
16 |
17 | @override
18 | void didPush(Route route, Route? previousRoute) {
19 | super.didPush(route, previousRoute);
20 | _didRouteChange(RouteAction.push, route, previousRoute);
21 | history.add(route);
22 | }
23 |
24 | @override
25 | void didRemove(Route route, Route? previousRoute) {
26 | super.didRemove(route, previousRoute);
27 | _didRouteChange(RouteAction.remove, previousRoute, route);
28 | final bool removed = history.remove(route);
29 | if (!removed) {
30 | history.removeLast();
31 | }
32 | }
33 |
34 | @override
35 | void didReplace({Route? newRoute, Route? oldRoute}) {
36 | super.didReplace(newRoute: newRoute, oldRoute: oldRoute);
37 | _didRouteChange(RouteAction.replace, newRoute, oldRoute);
38 | if (oldRoute != null) {
39 | final int index = history.indexOf(oldRoute);
40 | if (index != -1) {
41 | if (newRoute == null) {
42 | history.remove(oldRoute);
43 | } else {
44 | history[index] = newRoute;
45 | }
46 | }
47 | } else if (oldRoute == null && newRoute != null) {
48 | history.add(newRoute);
49 | }
50 | }
51 |
52 | void _didRouteChange(
53 | RouteAction action,
54 | Route? newRoute,
55 | Route? oldRoute,
56 | ) {
57 | LogUtil.dd(
58 | () => '[${action.name.toUpperCase().padLeft(7)}] '
59 | '${oldRoute?.settings.name} => ${newRoute?.settings.name}',
60 | tag: runtimeType.toString(),
61 | );
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/lib/internals/special_text.dart:
--------------------------------------------------------------------------------
1 | // Copyright 2022 The FlutterCandies author. All rights reserved.
2 | // Use of this source code is governed by a MIT license that can be found in the
3 | // LICENSE file.
4 |
5 | import 'package:extended_text/extended_text.dart';
6 | import 'package:flutter/gestures.dart';
7 | import 'package:flutter/material.dart';
8 |
9 | import '../constants/themes.dart';
10 |
11 | class TopicText extends RegExpSpecialText {
12 | /// [7118934098294341672#理财经验分享#]
13 | ///
14 | /// - Match numeric ID at start.
15 | /// - Match paired #.
16 | /// - Match not empty content between pairing #.
17 | @override
18 | final RegExp regExp = RegExp(r'\[(\d{18,})#(\S+?|\S((?!#\]).)+?\S)#\]');
19 |
20 | @override
21 | InlineSpan finishText(
22 | int start,
23 | Match match, {
24 | TextStyle? textStyle,
25 | SpecialTextGestureTapCallback? onTap,
26 | }) {
27 | final String actualText = toString();
28 | return SpecialTextSpan(
29 | text: '#${match.group(2)}#',
30 | actualText: actualText,
31 | start: start,
32 | style: (textStyle ?? const TextStyle()).copyWith(
33 | color: themeColorLight,
34 | ),
35 | recognizer: onTap != null
36 | ? (TapGestureRecognizer()..onTap = () => onTap(actualText))
37 | : null,
38 | );
39 | }
40 | }
41 |
42 | class JJRegExpSpecialTextSpanBuilder extends RegExpSpecialTextSpanBuilder {
43 | @override
44 | List get regExps {
45 | return [TopicText()];
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/lib/internals/urls.dart:
--------------------------------------------------------------------------------
1 | // Copyright 2022 The FlutterCandies author. All rights reserved.
2 | // Use of this source code is governed by a MIT license that can be found in the
3 | // LICENSE file.
4 |
5 | import 'package:url_launcher/url_launcher_string.dart';
6 |
7 | class Urls {
8 | const Urls._();
9 |
10 | static const String domain = 'juejin.cn';
11 | static const String apiHost = 'api.$domain';
12 |
13 | static Future canLaunch(String urlString) {
14 | return canLaunchUrlString(urlString);
15 | }
16 |
17 | static Future launchOnDevice(
18 | String urlString, {
19 | LaunchMode mode = LaunchMode.externalApplication,
20 | WebViewConfiguration webViewConfiguration = const WebViewConfiguration(),
21 | String? webOnlyWindowName,
22 | }) {
23 | return launchUrlString(
24 | urlString,
25 | mode: mode,
26 | webViewConfiguration: webViewConfiguration,
27 | webOnlyWindowName: webOnlyWindowName,
28 | );
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/lib/l10n/app_en.arb:
--------------------------------------------------------------------------------
1 | {
2 | "@@locale": "en",
3 | "appTitle": "Juejin",
4 |
5 | "exceptionAuthenticationExpired": "Authentication has expired, please login manually.",
6 | "exceptionError": "error",
7 | "exceptionErrorWidget": "Exception thrown during building this widget...",
8 | "exceptionFailed": "failed",
9 | "exceptionPoorNetwork": "Poor network condition, please retry later.",
10 | "exceptionRequest": "Request error: (-1 {message})",
11 | "@exceptionRequest": {"placeholders": {"message": {}}},
12 | "exceptionRouteNotFound": "{routeName} route not found.",
13 | "@exceptionRouteNotFound": {"placeholders": {"routeName": {"type": "String"}}},
14 | "exceptionRouteUnknown": "Unknown",
15 | "notSupported": "Not supported yet",
16 |
17 | "close": "Close",
18 |
19 | "listEmpty": "It's empty here.",
20 | "listLoadingMore": "Loading more content...",
21 | "listNetworkErrorClickRetry": "Network exceptions thrown. Click to retry.",
22 | "listNoMoreToLoad": "Nothing left.",
23 | "listRefreshing": "Refreshing...",
24 | "listRefreshWaiting": "Pull down more to refresh",
25 | "listRefreshArmed": "Release to refresh",
26 | "listRefreshSucceed": "Refreshed with new contents",
27 | "listRefreshFailed": "Failed to refresh",
28 |
29 | "webViewTitle": "Web page",
30 |
31 | "navHome": "Home",
32 | "navPins": "Pins",
33 | "navMe": "Me",
34 |
35 | "actionLike": "Like",
36 | "actionComment": "Comment",
37 | "actionReply": "Reply",
38 | "actionMore": "More",
39 | "actionFold": "Fold",
40 | "actionCollect": "Favorite",
41 | "actionShare": "Share",
42 | "advertiseAbbr": "Ad",
43 |
44 | "articleViews": "{num} views",
45 | "@articleViews": {
46 | "placeholders": {"num": {"type": "int"}}
47 | },
48 |
49 | "pinLiked": "and others liked",
50 | "pinHotCommentLikes": "{num} likes",
51 | "@pinHotCommentLikes": {
52 | "placeholders": {"num": {"type": "int"}}
53 | },
54 | "pinTotalCommentCount": "Total comments {count}",
55 | "@pinTotalCommentCount": {
56 | "placeholders": {"count": {"type": "int"}}
57 | },
58 |
59 | "userLikes": "Likes",
60 | "userFavorites": "Favorites",
61 | "userFollows": "Follows",
62 | "userComments": "Comments",
63 | "userFollowing": "Following",
64 | "userNotFollow": "Not follow",
65 | "userSignInOrUp": "Sign in/Sign up",
66 |
67 | "arrangement": "Sort By",
68 | "recommend": "Hot",
69 | "latest": "Latest",
70 |
71 |
72 | "join": "Join",
73 |
74 | "categoryTabFollowing": "Following",
75 | "categoryTabRecommend": "Recommend",
76 |
77 | "tagAll": "All",
78 | "sortRecommend": "Recommend",
79 | "sortLatest": "Latest",
80 |
81 | "durationYears": "{many, plural, =1{1 year} other{{many} years}} ago",
82 | "@durationYears": {
83 | "placeholders": {"many": {"type": "int"}}
84 | },
85 | "durationMonths": "{many, plural, =1{1 month} other{{many} months}} ago",
86 | "@durationMonths": {
87 | "placeholders": {"many": {"type": "int"}}
88 | },
89 | "durationDays": "{many, plural, =1{1 day} other{{many} days}} ago",
90 | "@durationDays": {
91 | "placeholders": {"many": {"type": "int"}}
92 | },
93 | "durationHours": "{many, plural, =1{1 hour} other{{many} hours}} ago",
94 | "@durationHours": {
95 | "placeholders": {"many": {"type": "int"}}
96 | },
97 | "durationMinutes": "{many, plural, =0{Just now} =1{1 minute ago} other{{many} minutes ago}}",
98 | "@durationMinutes": {
99 | "placeholders": {"many": {"type": "int"}}
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/lib/l10n/app_zh.arb:
--------------------------------------------------------------------------------
1 | {
2 | "@@locale": "zh",
3 | "appTitle": "掘金",
4 |
5 | "exceptionAuthenticationExpired": "身份已失效,请重新登录",
6 | "exceptionError": "错误",
7 | "exceptionErrorWidget": "构建时遇到未知错误...",
8 | "exceptionFailed": "失败",
9 | "exceptionPoorNetwork": "网络状况差,请稍后重试",
10 | "exceptionRequest": "请求失败:(-1 {message})",
11 | "exceptionRouteNotFound": "{routeName} 无效路由。",
12 | "exceptionRouteUnknown": "未知",
13 | "notSupported": "尚未实现",
14 |
15 | "close": "关闭",
16 |
17 | "listEmpty": "空空如也",
18 | "listLoadingMore": "加载中...",
19 | "listNetworkErrorClickRetry": "网络出错了~点此重试",
20 | "listNoMoreToLoad": "已经到底啦~",
21 | "listRefreshing": "正在刷新...",
22 | "listRefreshWaiting": "下拉更多以刷新",
23 | "listRefreshArmed": "松手以刷新",
24 | "listRefreshSucceed": "刷新成功",
25 | "listRefreshFailed": "刷新失败",
26 |
27 | "webViewTitle": "网页链接",
28 |
29 | "navHome": "首页",
30 | "navPins": "沸点",
31 | "navMe": "我",
32 |
33 | "actionLike": "赞",
34 | "actionComment": "评论",
35 | "actionReply": "回复",
36 | "actionMore": "展开",
37 | "actionFold": "收起",
38 | "actionCollect": "收藏",
39 | "actionShare": "分享",
40 | "advertiseAbbr": "广告",
41 |
42 | "articleViews": "阅读 {num}",
43 |
44 | "pinLiked": "等人赞过",
45 | "pinHotCommentLikes": "{num}人赞",
46 | "pinTotalCommentCount": "全部评论 {count}",
47 |
48 | "userLikes": "点赞",
49 | "userFavorites": "收藏",
50 | "userFollows": "关注",
51 | "userComments": "评论",
52 | "userFollowing": "已关注",
53 | "userNotFollow": "未关注",
54 | "userSignInOrUp": "登录/注册",
55 |
56 | "arrangement": "排序",
57 | "recommend": "热门",
58 | "latest": "最新",
59 |
60 | "categoryTabFollowing": "关注",
61 | "categoryTabRecommend": "推荐",
62 |
63 | "tagAll": "全部",
64 | "sortRecommend": "推荐",
65 | "sortLatest": "最新",
66 |
67 | "join": "加入",
68 |
69 | "durationYears": "{many, plural, other{{many}年前}}",
70 | "durationMonths": "{many, plural, other{{many}月前}}",
71 | "durationDays": "{many, plural, other{{many}天前}}",
72 | "durationHours": "{many, plural, other{{many}小时前}}",
73 | "durationMinutes": "{many, plural,=0{刚刚} other{{many}分钟前}}"
74 | }
75 |
--------------------------------------------------------------------------------
/lib/main.dart:
--------------------------------------------------------------------------------
1 | // Copyright 2022 The FlutterCandies author. All rights reserved.
2 | // Use of this source code is governed by a MIT license that can be found in the
3 | // LICENSE file.
4 |
5 | import 'package:flutter/foundation.dart';
6 | import 'package:flutter/material.dart';
7 |
8 | import 'app.dart';
9 | import 'exports.dart';
10 |
11 | void main() {
12 | PlatformDispatcher.instance.onError = (e, s) {
13 | if (e is ModelError) {
14 | return true;
15 | }
16 | if (e is DioException && e.type == DioExceptionType.cancel) {
17 | return true;
18 | }
19 | HapticUtil.notifyFailure();
20 | LogUtil.e(
21 | 'Caught unhandled exception: $e',
22 | stackTrace: s,
23 | tag: '💂 PlatformDispatcher',
24 | );
25 | showErrorToast('$e');
26 | return true;
27 | };
28 | JJErrorWidget.takeOver();
29 | runApp(JJApp(key: JJ.appKey));
30 | }
31 |
--------------------------------------------------------------------------------
/lib/models/data_model.d.dart:
--------------------------------------------------------------------------------
1 | part of 'data_model.dart';
2 |
3 | final Map dataModelFactories = {
4 | EmptyDataModel: EmptyDataModel.fromJson,
5 | FeedModel: FeedModel.fromJson,
6 | AdvertiseItemModel: AdvertiseItemModel.fromJson,
7 | ArticleItemModel: ArticleItemModel.fromJson,
8 | ArticleInfo: ArticleInfo.fromJson,
9 | ArticleStatus: ArticleStatus.fromJson,
10 | ArticleExtra: ArticleExtra.fromJson,
11 | Category: Category.fromJson,
12 | Tag: Tag.fromJson,
13 | CommentItemModel: CommentItemModel.fromJson,
14 | CommentInfo: CommentInfo.fromJson,
15 | CommentReply: CommentReply.fromJson,
16 | ReplyInfo: ReplyInfo.fromJson,
17 | PinItemModel: PinItemModel.fromJson,
18 | PinInfo: PinInfo.fromJson,
19 | PinTopic: PinTopic.fromJson,
20 | PinTheme: PinTheme.fromJson,
21 | HotComment: HotComment.fromJson,
22 | UserInfoModel: UserInfoModel.fromJson,
23 | UserUniversity: UserUniversity.fromJson,
24 | UserMajor: UserMajor.fromJson,
25 | UserGrowthInfo: UserGrowthInfo.fromJson,
26 | UserInteract: UserInteract.fromJson,
27 | UserOrg: UserOrg.fromJson,
28 | };
29 |
--------------------------------------------------------------------------------
/lib/models/data_model.dart:
--------------------------------------------------------------------------------
1 | // Copyright 2022 The FlutterCandies author. All rights reserved.
2 | // Use of this source code is governed by a MIT license that can be found in the
3 | // LICENSE file.
4 |
5 | import 'package:equatable/equatable.dart';
6 | import 'package:flutter/material.dart';
7 | import 'package:json_annotation/json_annotation.dart';
8 |
9 | import '../constants/constants.dart';
10 | import '../constants/resources.dart';
11 | import '../extensions/build_context_extension.dart';
12 | import '../extensions/duration_extension.dart';
13 | import '../extensions/num_extension.dart';
14 | import '../utils/log_util.dart';
15 |
16 | part 'data_model.d.dart';
17 | part 'data_model.g.dart';
18 | part 'feed_model.dart';
19 | part 'item/advertise_item_model.dart';
20 | part 'item/article_item_model.dart';
21 | part 'item/comment_item_model.dart';
22 | part 'item/post_item_model.dart';
23 | part 'user_model.dart';
24 |
25 | typedef Json = Map;
26 | typedef JsonList = List;
27 | typedef QueryMap = Map;
28 |
29 | abstract class DataModel extends Equatable {
30 | const DataModel();
31 |
32 | Json toJson();
33 |
34 | @override
35 | String toString() => globalJsonEncoder.convert(toJson());
36 | }
37 |
38 | typedef DataFactory = T Function(Json json);
39 |
40 | T makeModel(Json json) {
41 | if (!dataModelFactories.containsKey(T)) {
42 | LogUtil.e(
43 | "You're inflating an unregistered type: $T\n"
44 | "Please check if it's registered in `dataModelFactories`.",
45 | tag: '🏭 DataModel',
46 | );
47 | throw ModelNotRegisteredError();
48 | }
49 | try {
50 | return dataModelFactories[T]!(json) as T;
51 | } catch (e, s) {
52 | LogUtil.e(
53 | 'Error when making model with $T type: $e\n'
54 | '${json.containsKey('id') ? 'Model contains id: ${json['id']}\n' : ''}'
55 | 'The raw data which make this error is: '
56 | '${globalJsonEncoder.convert(json)}',
57 | stackTrace: s,
58 | tag: '🏭 DataModel',
59 | );
60 | throw ModelMakeError(json, e);
61 | }
62 | }
63 |
64 | class EmptyDataModel extends DataModel {
65 | const EmptyDataModel();
66 |
67 | factory EmptyDataModel.fromJson(dynamic _) => const EmptyDataModel();
68 |
69 | @override
70 | Json toJson() => const {};
71 |
72 | @override
73 | List