├── lib
├── locale
│ ├── i18n_en.json
│ └── i18n_zh.json
├── utils
│ ├── login_event.dart
│ ├── collect_event.dart
│ ├── refresh_event.dart
│ ├── loginout_event.dart
│ ├── error_event.dart
│ ├── clipboard_utils.dart
│ ├── Config.dart
│ ├── cookie_util.dart
│ ├── widget_utils.dart
│ ├── sp_util.dart
│ └── common.dart
├── page
│ ├── input
│ │ ├── page.dart
│ │ └── single_tag.dart
│ ├── feedback
│ │ └── network_error.dart
│ ├── todo
│ │ └── todo_fragment.dart
│ ├── account
│ │ └── login_fragment.dart
│ ├── system
│ │ ├── system_fragment.dart
│ │ ├── knowledge
│ │ │ ├── KnowledgeListFragment.dart
│ │ │ └── KnowledgeFragment.dart
│ │ └── navigation
│ │ │ └── NavigationFragment.dart
│ ├── tabs.dart
│ ├── wechat
│ │ ├── wechat_fragment.dart
│ │ └── wechat_list_fragment.dart
│ ├── project
│ │ ├── project_fragment.dart
│ │ └── project_list_fragment.dart
│ ├── wenda
│ │ └── wenda_fragment.dart
│ ├── home
│ │ └── search_result_fragment.dart
│ ├── rank
│ │ └── rank_fragment.dart
│ ├── share
│ │ └── share_article_fragment.dart
│ ├── square
│ │ └── square_fragment.dart
│ └── collect
│ │ └── collect_fragment.dart
├── application.dart
├── theme
│ ├── theme_model.dart
│ ├── dark_model.dart
│ ├── font_model.dart
│ ├── theme_colors.dart
│ └── locale_model.dart
├── widget
│ ├── article_title.dart
│ ├── icon_text.dart
│ ├── animate_provider.dart
│ ├── load_fail_widget.dart
│ ├── favourite_animation.dart
│ ├── custom_refresh.dart
│ ├── login_widget.dart
│ ├── coin_item.dart
│ ├── marquee_widget.dart
│ ├── title_bar.dart
│ ├── page_widget.dart
│ ├── share_item.dart
│ └── collect_item.dart
├── http
│ ├── base_response.dart
│ ├── api.dart
│ └── http_request.dart
├── data
│ ├── hot_key.dart
│ ├── rank.dart
│ ├── banner.dart
│ ├── article_response.dart
│ ├── project_tab.dart
│ ├── wechat_tab.dart
│ ├── todo.dart
│ ├── knowledge.dart
│ ├── login.dart
│ ├── navigation.dart
│ └── article.dart
├── constant
│ └── Constants.dart
├── routes
│ └── routes.dart
├── l10n
│ ├── intl_zh.arb
│ └── intl_en.arb
├── splash.dart
├── generated
│ └── intl
│ │ ├── messages_all.dart
│ │ ├── messages_zh.dart
│ │ └── messages_en.dart
├── main_page.dart
└── main.dart
├── android
├── settings_aar.gradle
├── key.properties
├── .gitignore
├── app
│ ├── src
│ │ ├── main
│ │ │ ├── res
│ │ │ │ ├── mipmap-hdpi
│ │ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-mdpi
│ │ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-xhdpi
│ │ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-xxhdpi
│ │ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-xxxhdpi
│ │ │ │ │ └── ic_launcher.png
│ │ │ │ ├── values
│ │ │ │ │ └── styles.xml
│ │ │ │ └── drawable
│ │ │ │ │ └── launch_background.xml
│ │ │ ├── kotlin
│ │ │ │ └── com
│ │ │ │ │ └── example
│ │ │ │ │ └── wanandroidflutter
│ │ │ │ │ └── MainActivity.kt
│ │ │ └── AndroidManifest.xml
│ │ ├── debug
│ │ │ └── AndroidManifest.xml
│ │ └── profile
│ │ │ └── AndroidManifest.xml
│ └── build.gradle
├── gradle.properties
├── gradle
│ └── wrapper
│ │ └── gradle-wrapper.properties
├── settings.gradle
├── build.gradle
└── wanandroidflutter_android.iml
├── ios
├── Flutter
│ ├── Debug.xcconfig
│ ├── Release.xcconfig
│ └── AppFrameworkInfo.plist
├── Runner
│ ├── Runner-Bridging-Header.h
│ ├── Assets.xcassets
│ │ ├── LaunchImage.imageset
│ │ │ ├── LaunchImage.png
│ │ │ ├── LaunchImage@2x.png
│ │ │ ├── LaunchImage@3x.png
│ │ │ ├── README.md
│ │ │ └── Contents.json
│ │ └── AppIcon.appiconset
│ │ │ ├── Icon-App-20x20@1x.png
│ │ │ ├── Icon-App-20x20@2x.png
│ │ │ ├── Icon-App-20x20@3x.png
│ │ │ ├── Icon-App-29x29@1x.png
│ │ │ ├── Icon-App-29x29@2x.png
│ │ │ ├── Icon-App-29x29@3x.png
│ │ │ ├── Icon-App-40x40@1x.png
│ │ │ ├── Icon-App-40x40@2x.png
│ │ │ ├── Icon-App-40x40@3x.png
│ │ │ ├── Icon-App-60x60@2x.png
│ │ │ ├── Icon-App-60x60@3x.png
│ │ │ ├── Icon-App-76x76@1x.png
│ │ │ ├── Icon-App-76x76@2x.png
│ │ │ ├── Icon-App-1024x1024@1x.png
│ │ │ ├── Icon-App-83.5x83.5@2x.png
│ │ │ └── Contents.json
│ ├── AppDelegate.swift
│ ├── Base.lproj
│ │ ├── Main.storyboard
│ │ └── LaunchScreen.storyboard
│ └── Info.plist
├── Runner.xcworkspace
│ └── contents.xcworkspacedata
├── Runner.xcodeproj
│ ├── project.xcworkspace
│ │ └── contents.xcworkspacedata
│ └── xcshareddata
│ │ └── xcschemes
│ │ └── Runner.xcscheme
└── .gitignore
├── assets
├── flrs
│ └── like.flr
├── img
│ ├── ic_back.png
│ ├── ic_avatar.png
│ ├── load_fail.png
│ ├── ic_default.png
│ └── load_no_data.png
└── fonts
│ ├── NotoSans-Regular.ttf
│ └── ZCOOLKuaiLe-Regular.ttf
├── .gitignore
├── wanandroidflutter.iml
├── pubspec.yaml
└── README.md
/lib/locale/i18n_en.json:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/lib/locale/i18n_zh.json:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/android/settings_aar.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------
/ios/Flutter/Debug.xcconfig:
--------------------------------------------------------------------------------
1 | #include "Generated.xcconfig"
2 |
--------------------------------------------------------------------------------
/ios/Flutter/Release.xcconfig:
--------------------------------------------------------------------------------
1 | #include "Generated.xcconfig"
2 |
--------------------------------------------------------------------------------
/ios/Runner/Runner-Bridging-Header.h:
--------------------------------------------------------------------------------
1 | #import "GeneratedPluginRegistrant.h"
--------------------------------------------------------------------------------
/lib/utils/login_event.dart:
--------------------------------------------------------------------------------
1 | class LoginEvent {
2 | LoginEvent();
3 | }
4 |
--------------------------------------------------------------------------------
/lib/utils/collect_event.dart:
--------------------------------------------------------------------------------
1 | class CollectEvent {
2 | CollectEvent();
3 | }
4 |
--------------------------------------------------------------------------------
/lib/utils/refresh_event.dart:
--------------------------------------------------------------------------------
1 | class RefreshEvent {
2 | RefreshEvent();
3 | }
4 |
--------------------------------------------------------------------------------
/lib/utils/loginout_event.dart:
--------------------------------------------------------------------------------
1 | class LoginOutEvent {
2 | LoginOutEvent();
3 | }
4 |
--------------------------------------------------------------------------------
/assets/flrs/like.flr:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wangjianxiandev/WanAndroidFlutter/HEAD/assets/flrs/like.flr
--------------------------------------------------------------------------------
/assets/img/ic_back.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wangjianxiandev/WanAndroidFlutter/HEAD/assets/img/ic_back.png
--------------------------------------------------------------------------------
/assets/img/ic_avatar.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wangjianxiandev/WanAndroidFlutter/HEAD/assets/img/ic_avatar.png
--------------------------------------------------------------------------------
/assets/img/load_fail.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wangjianxiandev/WanAndroidFlutter/HEAD/assets/img/load_fail.png
--------------------------------------------------------------------------------
/assets/img/ic_default.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wangjianxiandev/WanAndroidFlutter/HEAD/assets/img/ic_default.png
--------------------------------------------------------------------------------
/assets/img/load_no_data.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wangjianxiandev/WanAndroidFlutter/HEAD/assets/img/load_no_data.png
--------------------------------------------------------------------------------
/android/key.properties:
--------------------------------------------------------------------------------
1 | storePassword=wjx102519
2 | keyPassword=wjx102519
3 | keyAlias=key0
4 | storeFile=D:\\Android\\wjx.jks
--------------------------------------------------------------------------------
/assets/fonts/NotoSans-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wangjianxiandev/WanAndroidFlutter/HEAD/assets/fonts/NotoSans-Regular.ttf
--------------------------------------------------------------------------------
/assets/fonts/ZCOOLKuaiLe-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wangjianxiandev/WanAndroidFlutter/HEAD/assets/fonts/ZCOOLKuaiLe-Regular.ttf
--------------------------------------------------------------------------------
/lib/page/input/page.dart:
--------------------------------------------------------------------------------
1 | class Page {
2 | final String labelId;
3 | final int labelIndex;
4 |
5 | Page(this.labelId, this.labelIndex);
6 | }
7 |
--------------------------------------------------------------------------------
/android/.gitignore:
--------------------------------------------------------------------------------
1 | gradle-wrapper.jar
2 | /.gradle
3 | /captures/
4 | /gradlew
5 | /gradlew.bat
6 | /local.properties
7 | GeneratedPluginRegistrant.java
8 |
--------------------------------------------------------------------------------
/lib/utils/error_event.dart:
--------------------------------------------------------------------------------
1 | class ErrorEvent {
2 | int errorCode;
3 | String errorMessage;
4 |
5 | ErrorEvent(this.errorCode, this.errorMessage);
6 | }
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wangjianxiandev/WanAndroidFlutter/HEAD/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wangjianxiandev/WanAndroidFlutter/HEAD/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wangjianxiandev/WanAndroidFlutter/HEAD/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wangjianxiandev/WanAndroidFlutter/HEAD/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/gradle.properties:
--------------------------------------------------------------------------------
1 | org.gradle.jvmargs=-Xmx1536M
2 | android.enableR8=true
3 | android.useAndroidX=true
4 | android.enableJetifier=true
5 | android.enableAapt2=false
6 |
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wangjianxiandev/WanAndroidFlutter/HEAD/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wangjianxiandev/WanAndroidFlutter/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wangjianxiandev/WanAndroidFlutter/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wangjianxiandev/WanAndroidFlutter/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wangjianxiandev/WanAndroidFlutter/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wangjianxiandev/WanAndroidFlutter/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wangjianxiandev/WanAndroidFlutter/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wangjianxiandev/WanAndroidFlutter/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wangjianxiandev/WanAndroidFlutter/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wangjianxiandev/WanAndroidFlutter/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wangjianxiandev/WanAndroidFlutter/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wangjianxiandev/WanAndroidFlutter/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wangjianxiandev/WanAndroidFlutter/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wangjianxiandev/WanAndroidFlutter/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wangjianxiandev/WanAndroidFlutter/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wangjianxiandev/WanAndroidFlutter/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wangjianxiandev/WanAndroidFlutter/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wangjianxiandev/WanAndroidFlutter/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wangjianxiandev/WanAndroidFlutter/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png
--------------------------------------------------------------------------------
/ios/Runner.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/lib/application.dart:
--------------------------------------------------------------------------------
1 | import 'package:event_bus/event_bus.dart';
2 | import 'package:wanandroidflutter/utils/sp_util.dart';
3 |
4 | /// 配置全局变量
5 | class Application {
6 | static EventBus eventBus;
7 | static SpUtil sp;
8 | }
9 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Fri Jun 23 08:50:38 CEST 2017
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip
7 |
--------------------------------------------------------------------------------
/lib/page/feedback/network_error.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | class NetWorkErrorPage extends StatelessWidget {
3 | @override
4 | Widget build(BuildContext context) {
5 | return Scaffold(
6 | appBar: AppBar(
7 | title: Text('Error'),
8 | ),
9 | );
10 | }
11 | }
--------------------------------------------------------------------------------
/lib/theme/theme_model.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class ThemeModel with ChangeNotifier {
4 | Color _themeColor;
5 |
6 | get themeColor => _themeColor;
7 |
8 | void updateThemeColor(Color color) {
9 | this._themeColor = color;
10 | notifyListeners();
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/lib/page/todo/todo_fragment.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class TodoPage extends StatefulWidget {
4 | @override
5 | _TodoPageState createState() => _TodoPageState();
6 | }
7 |
8 | class _TodoPageState extends State {
9 | @override
10 | Widget build(BuildContext context) {
11 | return Container();
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/android/app/src/debug/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/android/app/src/profile/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md:
--------------------------------------------------------------------------------
1 | # Launch Screen Assets
2 |
3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory.
4 |
5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.
--------------------------------------------------------------------------------
/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 |
--------------------------------------------------------------------------------
/lib/utils/clipboard_utils.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/services.dart';
2 |
3 | class ClipboardUtil {
4 | static saveData2Clipboard(String text) {
5 | ClipboardData data = new ClipboardData(text: text);
6 | Clipboard.setData(data);
7 | }
8 |
9 | static Future getClipboardContents() async {
10 | var clipboardData = await Clipboard.getData(Clipboard.kTextPlain);
11 | return Future.value(clipboardData.text);
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/lib/theme/dark_model.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:wanandroidflutter/application.dart';
3 | import 'package:wanandroidflutter/utils/Config.dart';
4 |
5 | class DarkMode with ChangeNotifier {
6 | bool _isDark = false;
7 |
8 | get isDark => _isDark;
9 |
10 | void updateDarkMode(isDark) {
11 | _isDark = isDark;
12 | notifyListeners();
13 | Application.sp.putBool(Config.SP_DARK_MODEL, isDark);
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/ios/Runner/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 | import Flutter
3 |
4 | @UIApplicationMain
5 | @objc class AppDelegate: FlutterAppDelegate {
6 | override func application(
7 | _ application: UIApplication,
8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
9 | ) -> Bool {
10 | GeneratedPluginRegistrant.register(with: self)
11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions)
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/lib/theme/font_model.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:wanandroidflutter/application.dart';
3 | import 'package:wanandroidflutter/utils/Config.dart';
4 |
5 | class FontModel with ChangeNotifier {
6 | int _fontIndex;
7 |
8 | get fontIndex => _fontIndex;
9 |
10 | void updateFontIndex(int fontIndex) {
11 | this._fontIndex = fontIndex;
12 | notifyListeners();
13 | Application.sp.putInt(Config.SP_FONT_INDEX, fontIndex);
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/lib/widget/article_title.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_html/flutter_html.dart';
3 |
4 | class ArticleTitleWidget extends StatelessWidget {
5 | final String title;
6 |
7 | ArticleTitleWidget(this.title);
8 |
9 | @override
10 | Widget build(BuildContext context) {
11 | return Html(
12 | padding: EdgeInsets.symmetric(vertical: 5),
13 | data: title,
14 | defaultTextStyle: Theme.of(context).textTheme.subtitle,
15 | );
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/lib/utils/Config.dart:
--------------------------------------------------------------------------------
1 | class Config {
2 | static final SP_PWD = "pwd";
3 | static final SP_USER_INFO = "userInfo";
4 | static final SP_SEARCH_HISTORY = "search_history";
5 | static final SP_COIN = "coin_info";
6 | static final SP_THEME_COLOR = "theme_color";
7 | static final SP_BEFORE_CHANGE_DARK_MODE = "color_before";
8 | static final SP_DARK_MODEL = "dark_model";
9 | static final SP_FONT_INDEX = "font_index";
10 | static final SP_LOCALE_INDEX = "locale_idnex";
11 | static final SP_AVATAR = "avatar";
12 | }
13 |
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/android/app/src/main/kotlin/com/example/wanandroidflutter/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.example.wanandroidflutter
2 |
3 | import androidx.annotation.NonNull;
4 | import io.flutter.embedding.android.FlutterActivity
5 | import io.flutter.embedding.engine.FlutterEngine
6 | import io.flutter.plugins.GeneratedPluginRegistrant
7 |
8 | class MainActivity: FlutterActivity() {
9 | override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
10 | GeneratedPluginRegistrant.registerWith(flutterEngine);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/android/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
3 | def flutterProjectRoot = rootProject.projectDir.parentFile.toPath()
4 |
5 | def plugins = new Properties()
6 | def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins')
7 | if (pluginsFile.exists()) {
8 | pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) }
9 | }
10 |
11 | plugins.each { name, path ->
12 | def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile()
13 | include ":$name"
14 | project(":$name").projectDir = pluginDirectory
15 | }
16 |
--------------------------------------------------------------------------------
/lib/http/base_response.dart:
--------------------------------------------------------------------------------
1 | class BaseResponse {
2 | Object data;
3 | int errorCode;
4 | String errorMessage;
5 |
6 | BaseResponse.fromJson(Map json) {
7 | data = json["data"];
8 | errorCode = json["errorCode"];
9 | errorMessage = json["errorMessage"];
10 | }
11 |
12 | Map toJson() {
13 | final Map data = new Map();
14 | data["data"] = this.data;
15 | data["errorCode"] = this.errorCode;
16 | data["errorMessage"] = this.errorMessage;
17 | return data;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | .dart_tool/
3 |
4 | .packages
5 | .pub/
6 |
7 | .idea/
8 | .vagrant/
9 | .sconsign.dblite
10 | .svn/
11 |
12 | *.swp
13 | profile
14 |
15 | DerivedData/
16 |
17 | .generated/
18 |
19 | *.pbxuser
20 | *.mode1v3
21 | *.mode2v3
22 | *.perspectivev3
23 |
24 | !default.pbxuser
25 | !default.mode1v3
26 | !default.mode2v3
27 | !default.perspectivev3
28 |
29 | xcuserdata
30 |
31 | *.moved-aside
32 |
33 | *.pyc
34 | *sync/
35 | Icon?
36 | .tags*
37 |
38 | build/
39 | .android/
40 | .ios/
41 | .flutter-plugins
42 | .flutter-plugins-dependencies
43 | .gitattributes
44 | .metadata
45 | LICENSE
46 | captures/
47 |
48 |
--------------------------------------------------------------------------------
/lib/utils/cookie_util.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 | import 'dart:io';
3 | import 'package:cookie_jar/cookie_jar.dart';
4 | import 'package:path_provider/path_provider.dart';
5 |
6 | class CookieUtil {
7 | // 获取cookie地址
8 | static Future getCookiePath() async {
9 | Directory tempDir = await getTemporaryDirectory();
10 | String tempPath = tempDir.path;
11 | return "${tempPath}/cookies";
12 | }
13 |
14 | //清除所有cookie
15 | static Future deleteAllCookies() async {
16 | await getCookiePath().then((path) {
17 | PersistCookieJar cookieJar = PersistCookieJar(dir: path);
18 | cookieJar.deleteAll();
19 | });
20 | }
21 | }
--------------------------------------------------------------------------------
/lib/data/hot_key.dart:
--------------------------------------------------------------------------------
1 | class HotKey {
2 | int visible;
3 | String link;
4 | String name;
5 | int id;
6 | int order;
7 |
8 | HotKey.fromJson(Map json) {
9 | visible = json['visible'];
10 | link = json['link'];
11 | name = json['name'];
12 | id = json['id'];
13 | order = json['order'];
14 | }
15 |
16 | Map toJson() {
17 | final Map data = new Map();
18 | data['visible'] = this.visible;
19 | data['link'] = this.link;
20 | data['name'] = this.name;
21 | data['id'] = this.id;
22 | data['order'] = this.order;
23 | return data;
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/ios/.gitignore:
--------------------------------------------------------------------------------
1 | *.mode1v3
2 | *.mode2v3
3 | *.moved-aside
4 | *.pbxuser
5 | *.perspectivev3
6 | **/*sync/
7 | .sconsign.dblite
8 | .tags*
9 | **/.vagrant/
10 | **/DerivedData/
11 | Icon?
12 | **/Pods/
13 | **/.symlinks/
14 | profile
15 | xcuserdata
16 | **/.generated/
17 | Flutter/App.framework
18 | Flutter/Flutter.framework
19 | Flutter/Flutter.podspec
20 | Flutter/Generated.xcconfig
21 | Flutter/app.flx
22 | Flutter/app.zip
23 | Flutter/flutter_assets/
24 | Flutter/flutter_export_environment.sh
25 | ServiceDefinitions.json
26 | Runner/GeneratedPluginRegistrant.*
27 |
28 | # Exceptions to above rules.
29 | !default.mode1v3
30 | !default.mode2v3
31 | !default.pbxuser
32 | !default.perspectivev3
33 |
--------------------------------------------------------------------------------
/lib/widget/icon_text.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class IconTextWidget extends StatelessWidget {
4 | Widget icon;
5 | Widget text;
6 | double padding;
7 |
8 | IconTextWidget({@required this.icon, @required this.text, this.padding=0});
9 |
10 | @override
11 | Widget build(BuildContext context) {
12 | return Center(
13 | child: Row(
14 | children: [
15 | Padding(
16 | padding: EdgeInsets.only(right: padding),
17 | child: icon,
18 | ),
19 | Expanded(
20 | flex: 1,
21 | child: text,
22 | ),
23 | ],
24 | ),
25 | );
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/lib/data/rank.dart:
--------------------------------------------------------------------------------
1 | class RankData {
2 | int level;
3 | int rank;
4 | int userId;
5 | int coinCount;
6 | String username;
7 |
8 | RankData.fromJson(Map json) {
9 | level = json['level'];
10 | rank = json['rank'];
11 | userId = json['userId'];
12 | coinCount = json['coinCount'];
13 | username = json['username'];
14 | }
15 |
16 | Map toJson() {
17 | final Map data = new Map();
18 | data['level'] = this.level;
19 | data['rank'] = this.rank;
20 | data['userId'] = this.userId;
21 | data['coinCount'] = this.coinCount;
22 | data['username'] = this.username;
23 | return data;
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/android/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | ext.kotlin_version = '1.3.50'
3 | repositories {
4 | google()
5 | jcenter()
6 | }
7 |
8 | dependencies {
9 | classpath 'com.android.tools.build:gradle:3.5.3'
10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
11 | }
12 | }
13 |
14 | allprojects {
15 | repositories {
16 | google()
17 | jcenter()
18 | }
19 | }
20 |
21 | rootProject.buildDir = '../build'
22 | subprojects {
23 | project.buildDir = "${rootProject.buildDir}/${project.name}"
24 | }
25 | subprojects {
26 | project.evaluationDependsOn(':app')
27 | }
28 |
29 | task clean(type: Delete) {
30 | delete rootProject.buildDir
31 | }
32 |
--------------------------------------------------------------------------------
/lib/theme/theme_colors.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/painting.dart';
2 |
3 | List getThemeColors() {
4 | List themeColors = new List();
5 | themeColors
6 | ..add(Color(0xffc54945)) //
7 | ..add(Color(0xfffc5e38)) //
8 | ..add(Color(0xfffd742d)) //
9 | ..add(Color(0xfff6b816)) //
10 | ..add(Color(0xffcae053)) //
11 | ..add(Color(0xff81c842)) //
12 | ..add(Color(0xff5cc095)) //
13 | ..add(Color(0xff1e88e5)) //
14 | ..add(Color(0xff5978e9)) //
15 | ..add(Color(0xff7668f6)) //
16 | ..add(Color(0xffa674e6)) //
17 | ..add(Color(0xffd477e6)) //
18 | ..add(Color(0xffec7ec5)) //
19 | ..add(Color(0xffed698b)) //
20 | ..add(Color(0xfff19fb4)) //
21 | ..add(Color(0xff323638));
22 | return themeColors;
23 | }
--------------------------------------------------------------------------------
/lib/constant/Constants.dart:
--------------------------------------------------------------------------------
1 | import 'package:wanandroidflutter/page/input/page.dart';
2 |
3 | class Constants {
4 | /// 网络错误
5 | static const NETWORK_ERROR = -1;
6 |
7 | /// 网络超时
8 | static const NETWORK_TIMEOUT = -2;
9 |
10 | /// 返回数据格式化
11 | static const NETWORK_JSON_EXCEPTION = -3;
12 |
13 | /// 成功
14 | static const SUCCESS = 200;
15 |
16 | //主题颜色
17 | static const String THEME_COLOR_KEY = 'theme_color_key';
18 |
19 | //是否为夜间模式
20 | static const String THEME_DARK_MODE_KEY = 'theme_dark_mode_key';
21 |
22 | // 字体列表
23 | static const FontList = ['normal', 'kuaile'];
24 |
25 | // 语言列表
26 | static const LocaleList = ['', 'zh-CN', 'en'];
27 |
28 | static final List todoTypes = [
29 | Page('只用这一个', 0),
30 | Page('工作', 1),
31 | Page('学习', 2),
32 | Page('生活', 3)
33 | ];
34 | }
35 |
--------------------------------------------------------------------------------
/lib/widget/animate_provider.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class ScaleAnimatedSwitcher extends StatelessWidget {
4 | final Widget child;
5 |
6 | ScaleAnimatedSwitcher({this.child});
7 |
8 | @override
9 | Widget build(BuildContext context) {
10 | return AnimatedSwitcher(
11 | duration: Duration(milliseconds: 300),
12 | transitionBuilder: (child, animated) => ScaleTransition(
13 | scale: animated,
14 | child: child,
15 | ),
16 | child: child,
17 | );
18 | }
19 | }
20 |
21 | class EmptyAnimatedSwitcher extends StatelessWidget {
22 | final bool display;
23 | final Widget child;
24 | const EmptyAnimatedSwitcher({Key key, this.display, this.child})
25 | : super(key: key);
26 | @override
27 | Widget build(BuildContext context) {
28 | return ScaleAnimatedSwitcher(child: display ? child : SizedBox.shrink());
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/lib/data/banner.dart:
--------------------------------------------------------------------------------
1 | class BannerData {
2 | String desc;
3 | int id;
4 | String imagePath;
5 | int isVisible;
6 | int order;
7 | String title;
8 | int type;
9 | String url;
10 |
11 | BannerData.fromJson(Map map) {
12 | desc = map["desc"];
13 | id = map["id"];
14 | imagePath = map["imagePath"];
15 | isVisible = map["isVisible"];
16 | order = map["order"];
17 | title = map["title"];
18 | type = map["type"];
19 | url = map["url"];
20 | }
21 |
22 | Map toJson() {
23 | final Map data = new Map();
24 | data["desc"] = this.desc;
25 | data["id"] = this.id;
26 | data["imagePath"] = this.imagePath;
27 | data["isVisible"] = this.isVisible;
28 | data["order"] = this.order;
29 | data["title"] = this.title;
30 | data["type"] = this.type;
31 | data["url"] = this.url;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/ios/Flutter/AppFrameworkInfo.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | App
9 | CFBundleIdentifier
10 | io.flutter.flutter.app
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | App
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1.0
23 | MinimumOSVersion
24 | 8.0
25 |
26 |
27 |
--------------------------------------------------------------------------------
/wanandroidflutter.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/lib/routes/routes.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/cupertino.dart';
2 | import 'package:flutter/material.dart';
3 | import 'package:wanandroidflutter/page/tabs.dart';
4 |
5 | // 增加要跳转的页面只需要在此处添加
6 | final routes = {
7 | // 配置根路由
8 | '/': (context, {argumment}) => Tabs(),
9 | };
10 |
11 | // 配置路由, 在官方代码上的优化,为固定写法
12 | var onGenerateRoute = (RouteSettings settings) {
13 | // ignore: missing_return
14 | final String name = settings.name;
15 | final Function pageContentBuilder = routes[name];
16 | if (pageContentBuilder != null) {
17 | // 路由配置不为空
18 | if (settings.arguments != null) {
19 | final Route route = MaterialPageRoute(
20 | // 进行跳转,传参
21 | builder: (context) =>
22 | pageContentBuilder(context, arguments: settings.arguments));
23 | return route;
24 | } else {
25 | final Route route =
26 | MaterialPageRoute(builder: (context) => pageContentBuilder(context));
27 | return route;
28 | }
29 | }
30 | };
31 |
--------------------------------------------------------------------------------
/lib/widget/load_fail_widget.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:wanandroidflutter/generated/l10n.dart';
3 |
4 | //加载失败widget
5 | class LoadFailWidget extends StatelessWidget {
6 | Function onTap;
7 |
8 | LoadFailWidget({this.onTap});
9 |
10 | @override
11 | Widget build(BuildContext context) {
12 | return GestureDetector(
13 | onTap: () {
14 | onTap();
15 | },
16 | child: Center(
17 | child: Column(
18 | mainAxisAlignment: MainAxisAlignment.center,
19 | children: [
20 | ImageIcon(
21 | AssetImage("assets/img/load_fail.png"),
22 | size: 50,
23 | ),
24 | SizedBox(
25 | height: 10,
26 | ),
27 | Text(
28 | S.of(context).network_error,
29 | style: Theme.of(context).textTheme.caption,
30 | ),
31 | ],
32 | ),
33 | ),
34 | );
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/lib/data/article_response.dart:
--------------------------------------------------------------------------------
1 | class ArticleResponse {
2 | int curPage;
3 | int offset;
4 | int pageCount;
5 | int size;
6 | int total;
7 | bool over;
8 | var datas;
9 |
10 | ArticleResponse.fromJson(Map json)
11 | : curPage = json["curPage"],
12 | offset = json["offset"],
13 | size = json["size"],
14 | total = json["total"],
15 | over = json["over"],
16 | pageCount = json["pageCount"],
17 | datas = json["datas"];
18 |
19 | Map toJson() => {
20 | "curPage": curPage,
21 | "offset": offset,
22 | "size": size,
23 | "total": total,
24 | "over": over,
25 | "pageCount": pageCount,
26 | "datas": datas,
27 | };
28 |
29 | @override
30 | String toString() {
31 | return 'ArticleResponse{curPage: $curPage, offset: $offset, pageCount: $pageCount, size: $size, total: $total, over: $over, datas: $datas}';
32 | }
33 |
34 | bool get hasNoMore => curPage == pageCount;
35 | }
36 |
--------------------------------------------------------------------------------
/lib/data/project_tab.dart:
--------------------------------------------------------------------------------
1 | class ProjectTab {
2 | int visible;
3 | List children;
4 | String name;
5 | bool userControlSetTop;
6 | int id;
7 | int courseId;
8 | int parentChapterId;
9 | int order;
10 |
11 | ProjectTab.fromJson(Map json) {
12 | visible = json['visible'];
13 | if (json['children'] != null) {
14 | children = new List();
15 | }
16 | name = json['name'];
17 | userControlSetTop = json['userControlSetTop'];
18 | id = json['id'];
19 | courseId = json['courseId'];
20 | parentChapterId = json['parentChapterId'];
21 | order = json['order'];
22 | }
23 |
24 | Map toJson() {
25 | final Map data = new Map();
26 | data['visible'] = this.visible;
27 | if (this.children != null) {
28 | data['children'] = [];
29 | }
30 | data['name'] = this.name;
31 | data['userControlSetTop'] = this.userControlSetTop;
32 | data['id'] = this.id;
33 | data['courseId'] = this.courseId;
34 | data['parentChapterId'] = this.parentChapterId;
35 | data['order'] = this.order;
36 | return data;
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/lib/data/wechat_tab.dart:
--------------------------------------------------------------------------------
1 | class WeChatTab {
2 | int visible;
3 | List children;
4 | String name;
5 | bool userControlSetTop;
6 | int id;
7 | int courseId;
8 | int parentChapterId;
9 | int order;
10 |
11 | WeChatTab.fromJson(Map json) {
12 | visible = json['visible'];
13 | if (json['children'] != null) {
14 | children = new List();
15 | }
16 | name = json['name'];
17 | userControlSetTop = json['userControlSetTop'];
18 | id = json['id'];
19 | courseId = json['courseId'];
20 | parentChapterId = json['parentChapterId'];
21 | order = json['order'];
22 | }
23 |
24 | Map toJson() {
25 | final Map data = new Map();
26 | data['visible'] = this.visible;
27 | if (this.children != null) {
28 | data['children'] = [];
29 | }
30 | data['name'] = this.name;
31 | data['userControlSetTop'] = this.userControlSetTop;
32 | data['id'] = this.id;
33 | data['courseId'] = this.courseId;
34 | data['parentChapterId'] = this.parentChapterId;
35 | data['order'] = this.order;
36 | return data;
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/lib/page/input/single_tag.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class SingleSelectTagView extends StatelessWidget {
4 |
5 | final int index; //为标识选中的
6 | final parent; //父控件
7 | final String choiceText;
8 |
9 |
10 | const SingleSelectTagView(
11 | {@required this.index,
12 | @required this.parent,
13 | @required this.choiceText})
14 | : super();
15 |
16 | @override
17 | Widget build(BuildContext context) {
18 | return Padding(
19 | padding: const EdgeInsets.all(8.0),
20 | child: ChoiceChip(
21 | label: Text(choiceText,style: TextStyle(color: Colors.white,fontWeight: FontWeight.bold)),
22 | //选定的时候背景
23 | selectedColor: Theme.of(context).primaryColor,
24 | //未选用得时候背景
25 | disabledColor: Colors.grey[300],
26 | labelStyle: TextStyle(fontWeight: FontWeight.w200, fontSize: 15.0),
27 | labelPadding: EdgeInsets.only(left: 8.0, right: 8.0),
28 | materialTapTargetSize: MaterialTapTargetSize.padded,
29 | onSelected: (bool value) {
30 | parent.onSelectedChanged(index);
31 | },
32 | selected: parent.selectedType == index),
33 | );
34 | }
35 | }
--------------------------------------------------------------------------------
/lib/data/todo.dart:
--------------------------------------------------------------------------------
1 | class TodoData {
2 | int completeDate;
3 | String completeDateStr;
4 | String content;
5 | int date;
6 | String dateStr;
7 | int id;
8 | int priority;
9 | int status;
10 | String title;
11 | int type;
12 | int userId;
13 |
14 | TodoData.fromJson(Map json) {
15 | completeDate = json["completeDate"];
16 | completeDateStr = json["completeDateStr"];
17 | content = json["content"];
18 | date = json["date"];
19 | dateStr = json["dateStr"];
20 | id = json["id"];
21 | priority = json["priority"];
22 | status = json["status"];
23 | title = json["title"];
24 | type = json["type"];
25 | userId = json["userId"];
26 | }
27 |
28 | Map toJson() {
29 | final Map data = Map();
30 | data["completeDate"] = this.completeDate;
31 | data["completeDateStr"] = this.completeDateStr;
32 | data["content"] = this.content;
33 | data["date"] = this.date;
34 | data["dateStr"] = this.dateStr;
35 | data["id"] = this.id;
36 | data["prority"] = this.priority;
37 | data["status"] = this.status;
38 | data["title"] = this.title;
39 | data["type"] = this.type;
40 | data["userId"] = this.userId;
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/lib/theme/locale_model.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:wanandroidflutter/application.dart';
3 | import 'package:wanandroidflutter/constant/Constants.dart';
4 | import 'package:wanandroidflutter/generated/l10n.dart';
5 | import 'package:wanandroidflutter/utils/Config.dart';
6 |
7 | class LocaleModel with ChangeNotifier {
8 | int _localeIndex;
9 |
10 | get localeIndex => _localeIndex;
11 |
12 | Locale get locale {
13 | if (_localeIndex > 0) {
14 | var value = Constants.LocaleList[_localeIndex].split("-");
15 | return Locale(value[0], value.length == 2 ? value[1] : '');
16 | }
17 | // 跟随系统
18 | return null;
19 | }
20 |
21 | LocaleModel() {
22 | _localeIndex = Application.sp.getInt(Config.SP_LOCALE_INDEX) ?? 0;
23 | }
24 |
25 | void updateLocaleIndex(int localeIndex) {
26 | this._localeIndex = localeIndex;
27 | notifyListeners();
28 | Application.sp.putInt(Config.SP_LOCALE_INDEX, localeIndex);
29 | }
30 |
31 | static String localeName(index, context) {
32 | switch (index) {
33 | case 0:
34 | return S.of(context).autoBySystem;
35 | case 1:
36 | return '中文';
37 | case 2:
38 | return 'English';
39 | default:
40 | return '';
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/lib/data/knowledge.dart:
--------------------------------------------------------------------------------
1 | class KnowledgeData {
2 | int visible;
3 | List children;
4 | String name;
5 | bool userControlSetTop;
6 | int id;
7 | int courseId;
8 | int parentChapterId;
9 | int order;
10 |
11 | KnowledgeData.fromJson(Map json) {
12 | visible = json['visible'];
13 | if (json['children'] != null) {
14 | children = new List();
15 | (json['children'] as List).forEach((v) {
16 | children.add(new KnowledgeData.fromJson(v));
17 | });
18 | }
19 | name = json['name'];
20 | userControlSetTop = json['userControlSetTop'];
21 | id = json['id'];
22 | courseId = json['courseId'];
23 | parentChapterId = json['parentChapterId'];
24 | order = json['order'];
25 | }
26 |
27 | Map toJson() {
28 | final Map data = new Map();
29 | data['visible'] = this.visible;
30 | if (this.children != null) {
31 | data['children'] = this.children.map((v) => v.toJson()).toList();
32 | }
33 | data['name'] = this.name;
34 | data['userControlSetTop'] = this.userControlSetTop;
35 | data['id'] = this.id;
36 | data['courseId'] = this.courseId;
37 | data['parentChapterId'] = this.parentChapterId;
38 | data['order'] = this.order;
39 | return data;
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/lib/data/login.dart:
--------------------------------------------------------------------------------
1 | class LoginData {
2 | String password;
3 | String publicName;
4 | List chapterTops;
5 | String icon;
6 | String nickname;
7 | bool admin;
8 | List collectIds;
9 | int id;
10 | int type;
11 | String email;
12 | String token;
13 | String username;
14 |
15 | LoginData.fromJson(Map json) {
16 | password = json['password'];
17 | publicName = json['publicName'];
18 | chapterTops = json['chapterTops']?.cast();
19 | icon = json['icon'];
20 | nickname = json['nickname'];
21 | admin = json['admin'];
22 | collectIds = json['collectIds']?.cast();
23 | id = json['id'];
24 | type = json['type'];
25 | email = json['email'];
26 | token = json['token'];
27 | username = json['username'];
28 | }
29 |
30 | Map toJson() {
31 | final Map data = new Map();
32 | data['password'] = this.password;
33 | data['publicName'] = this.publicName;
34 | data['chapterTops'] = this.chapterTops;
35 | data['icon'] = this.icon;
36 | data['nickname'] = this.nickname;
37 | data['admin'] = this.admin;
38 | data['collectIds'] = this.collectIds;
39 | data['id'] = this.id;
40 | data['type'] = this.type;
41 | data['email'] = this.email;
42 | data['token'] = this.token;
43 | data['username'] = this.username;
44 | return data;
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/android/wanandroidflutter_android.iml:
--------------------------------------------------------------------------------
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 |
--------------------------------------------------------------------------------
/lib/utils/widget_utils.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class WidgetUtils{
4 |
5 | static Widget buildStrokeWidget(String text , Color color, FontWeight fontWeight, double fontSize) {
6 | return Padding(
7 | child: StrokeWidget(
8 | strokeWidth: 0.5,
9 | edgeInsets: EdgeInsets.symmetric(horizontal: 2.0, vertical: 0.0),
10 | color: color,
11 | childWidget: Text(
12 | text,
13 | style: TextStyle(
14 | fontSize: fontSize,
15 | color: color,
16 | fontWeight: fontWeight),
17 | )
18 | ),
19 | padding: EdgeInsets.only(right: 5.0),
20 | );
21 | }
22 | }
23 |
24 | class StrokeWidget extends StatelessWidget {
25 | final Color color;
26 | final Widget childWidget;
27 | EdgeInsets edgeInsets;
28 | final double strokeWidth;
29 | final double strokeRadius;
30 |
31 | StrokeWidget(
32 | {Key key,
33 | @required this.childWidget,
34 | this.color = Colors.black,
35 | this.edgeInsets,
36 | this.strokeWidth = 1.0,
37 | this.strokeRadius = 5.0})
38 | : super(key: key) {
39 | if (null == edgeInsets)
40 | edgeInsets = EdgeInsets.symmetric(horizontal: 2.0, vertical: 0.0);
41 | }
42 |
43 | @override
44 | Widget build(BuildContext context) {
45 | return Container(
46 | padding: edgeInsets,
47 | decoration: BoxDecoration(
48 | border: Border.all(color: color, width: strokeWidth),
49 | borderRadius: BorderRadius.circular(strokeRadius)),
50 | child: childWidget);
51 | }
52 | }
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/lib/l10n/intl_zh.arb:
--------------------------------------------------------------------------------
1 | {
2 | "tab_home": "首页",
3 | "tab_system": "体系",
4 | "tab_wechat": "公众号",
5 | "tab_project": "项目",
6 | "tab_navigation": "导航",
7 | "share_to_square": "分享到广场",
8 | "collect": "收藏",
9 | "copy_link": "复制链接",
10 | "copy_tip": "复制成功",
11 | "exit": "退出",
12 | "level": "排名: ",
13 | "integral": "积分: ",
14 | "login_tip": "点击头像登录",
15 | "crop_image" : "裁剪头像",
16 | "square": "广场",
17 | "share": "分享",
18 | "me_share": "我的分享",
19 | "me_collect": "我的收藏",
20 | "wenda": "问答",
21 | "theme": "主题",
22 | "rank": "积分排行榜",
23 | "integral_rank": "积分排行",
24 | "loginout": "退出登录",
25 | "loginoutTip": "登出成功",
26 | "setting": "设置",
27 | "confirm": "确认",
28 | "cancel": "取消",
29 | "theme_choose": "主题颜色选择",
30 | "theme_tips": "夜间模式下不可以更改主题嗷~",
31 | "night_mode": "夜间模式",
32 | "switching_fonts": "切换字体",
33 | "normol_font": "正常字体",
34 | "kuaile_font": "喵趣字体",
35 | "language_setting": "语言设置",
36 | "autoBySystem": "跟随系统",
37 | "clear_cache": "清空缓存",
38 | "clear_cache_tip": "确定清除缓存吗?",
39 | "about": "关于",
40 | "my_blog": "我的博客",
41 | "register": "注册",
42 | "username": "请输入用户名",
43 | "password": "请输入密码",
44 | "repassword": "请输入密码",
45 | "login": "登录",
46 | "input_search": "请输入搜索关键字",
47 | "search_tip": "输入字段为空",
48 | "search": "搜索",
49 | "hot_search": "热门搜索",
50 | "search_history": "搜索记录",
51 | "clear_history": "清空",
52 | "new_article": "新",
53 | "top": "置顶",
54 | "Stranger": "匿名",
55 | "down_refresh" : "下拉刷新",
56 | "up_refresh" : "上拉刷新",
57 | "refreshing" : "刷新中",
58 | "update_time" : "更新时间: ",
59 | "release_refresh" : "释放刷新",
60 | "complete_refresh" : "刷新完成",
61 | "network_error": "网络异常,点击重试!",
62 | "no_data": "没有数据,请重试!"
63 | }
--------------------------------------------------------------------------------
/ios/Runner/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | wanandroidflutter
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | $(FLUTTER_BUILD_NAME)
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | $(FLUTTER_BUILD_NUMBER)
23 | LSRequiresIPhoneOS
24 |
25 | UILaunchStoryboardName
26 | LaunchScreen
27 | UIMainStoryboardFile
28 | Main
29 | UISupportedInterfaceOrientations
30 |
31 | UIInterfaceOrientationPortrait
32 | UIInterfaceOrientationLandscapeLeft
33 | UIInterfaceOrientationLandscapeRight
34 |
35 | UISupportedInterfaceOrientations~ipad
36 |
37 | UIInterfaceOrientationPortrait
38 | UIInterfaceOrientationPortraitUpsideDown
39 | UIInterfaceOrientationLandscapeLeft
40 | UIInterfaceOrientationLandscapeRight
41 |
42 | UIViewControllerBasedStatusBarAppearance
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/lib/l10n/intl_en.arb:
--------------------------------------------------------------------------------
1 | {
2 | "tab_home": "Home",
3 | "tab_system": "System",
4 | "tab_wechat": "WX",
5 | "tab_project": "Project",
6 | "tab_navigation": "Navigation",
7 | "share_to_square": "Share",
8 | "collect": "Collect",
9 | "copy_link": "CopyLink",
10 | "copy_tip": "CopySuccess",
11 | "exit": "Exit",
12 | "level": "level: ",
13 | "integral": "Integral: ",
14 | "login_tip": "Click avatar login",
15 | "crop_image" : "Crop image",
16 | "square": "Square",
17 | "me_share": "MeShare",
18 | "me_collect": "MeCollect",
19 | "wenda": "Q&A",
20 | "theme": "Theme",
21 | "rank": "Ranking",
22 | "integral_rank": "ranking",
23 | "loginout": "LoginOut",
24 | "loginoutTip": "Loginout Success",
25 | "setting": "Setting",
26 | "confirm": "Confirm",
27 | "cancel": "Cancel",
28 | "theme_choose": "Theme Choose",
29 | "theme_tips": "Cannot be changed in night mode~",
30 | "night_mode": "NightMode",
31 | "switching_fonts": "Fonts",
32 | "normol_font": "Normal",
33 | "kuaile_font": "MiaoQu",
34 | "language_setting": "Language",
35 | "autoBySystem": "Auto",
36 | "clear_cache": "ClearCache",
37 | "clear_cache_tip": "Are you sure?",
38 | "about": "About",
39 | "my_blog": "Blog",
40 | "register": "Register",
41 | "username": "Input username",
42 | "password": "Input password",
43 | "repassword": "Confirm password",
44 | "login": "Login",
45 | "input_search": "Input search key",
46 | "search_tip": "Input Empty",
47 | "search": "go",
48 | "hot_search": "HotSearch",
49 | "search_history": "SearchHistory",
50 | "clear_history": "Clear",
51 | "new_article": "New",
52 | "top": "Top",
53 | "stranger": "Stranger",
54 | "down_refresh" : "Down refresh",
55 | "up_refresh" : "Up refresh",
56 | "refreshing" : "refreshing",
57 | "update_time" : "update time: ",
58 | "release_refresh" : "release refresh",
59 | "complete_refresh" : "complete",
60 | "network_error": "Network error, please try again!",
61 | "no_data": "No data, please try again!"
62 | }
--------------------------------------------------------------------------------
/lib/page/account/login_fragment.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:provider/provider.dart';
3 | import 'package:wanandroidflutter/page/account/login_form.dart';
4 | import 'package:wanandroidflutter/page/account/register_form.dart';
5 | import 'package:wanandroidflutter/theme/theme_model.dart';
6 | import 'package:wanandroidflutter/widget/login_widget.dart';
7 |
8 | class LoginPage extends StatelessWidget {
9 | var _pageController = new PageController(initialPage: 0);
10 |
11 | @override
12 | Widget build(BuildContext context) {
13 | var appTheme = Provider.of(context);
14 | return Scaffold(
15 | appBar: AppBar(
16 | backgroundColor: appTheme.themeColor,
17 | leading: IconButton(
18 | icon: Icon(Icons.arrow_back),
19 | onPressed: () {
20 | Navigator.of(context).pop();
21 | },
22 | ),
23 | ),
24 | body: Stack(children: [
25 | Column(
26 | crossAxisAlignment: CrossAxisAlignment.center,
27 | children: [
28 | Expanded(
29 | flex: 2,
30 | child:
31 | Stack(
32 | children: [
33 | LoginTopPanel(),
34 | Align(
35 | alignment: Alignment.center,
36 | child: LoginLogo(),
37 | )
38 | ],
39 | ),
40 | ),
41 | Expanded(
42 | child: new PageView.builder(
43 | itemBuilder: (BuildContext context, int index) {
44 | return index == 0
45 | ? new LoginForm(_pageController)
46 | : new RegisterForm(_pageController);
47 | },
48 | itemCount: 2,
49 | controller: _pageController,
50 | ),
51 | flex: 4,
52 | )
53 | ]),
54 | ]));
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/lib/http/api.dart:
--------------------------------------------------------------------------------
1 | class Api {
2 | static const String BASE_URL = "https://www.wanandroid.com/";
3 |
4 | //首页banner
5 | static const String BANNER_URL = "banner/json";
6 |
7 | //置顶文章
8 | static const String ARTICLE_TOP = "article/top/json";
9 |
10 | //首页文章
11 | static const String HOME_ARTICLE_LIST = "article/list/";
12 |
13 | //搜索热词
14 | static const String HOT_KEY = "hotkey/json";
15 |
16 | //搜索
17 | static const String SEARCH_RESULT_LIST = "article/query/";
18 |
19 | //收藏站内文章
20 | static const String COLLECT = "lg/collect/";
21 |
22 | //取消收藏-文章列表
23 | static const String UN_COLLECT_ORIGIN_ID = "lg/uncollect_originId/";
24 |
25 | //登录
26 | static const String LOGIN = "user/login";
27 |
28 | //退出登录
29 | static const String LOGIN_OUT_JSON = 'user/logout/json';
30 |
31 | //注册
32 | static const String REGISTER = "user/register";
33 |
34 | //获取公众号Tab
35 | static const String WECHAT_TAB = "wxarticle/chapters/json";
36 |
37 | //获取公众号文章
38 | static const String WECHAT_LIST = "wxarticle/list/";
39 |
40 | //获取项目分类
41 | static const String PROJECT_TAB = "project/tree/json";
42 |
43 | //获取项目列表数据
44 | static const String PROJECT_LIST = "project/list/";
45 |
46 | //获取体系数据
47 | static const String TREE = "tree/json";
48 |
49 | //获取导航数据
50 | static const String NAVIGATION = "navi/json";
51 |
52 | //获取用户积分数据
53 | static const String COIN_INFO = "lg/coin/userinfo/json";
54 |
55 | //获取收藏文章列表
56 | static const String COLLECT_LIST = "lg/collect/list/";
57 |
58 | //取消收藏
59 | static const String UN_COLLECT = "lg/uncollect/";
60 |
61 | //添加站外收藏
62 | static const String ADD_COLLECT_ARTICLE = "lg/collect/add/json";
63 |
64 | //获取广场数据
65 | static const String SQUARE_LIST = "user_article/list/";
66 |
67 | //获取积分列表
68 | static const String RANK_LIST = "coin/rank/";
69 |
70 | //获取问答列表
71 | static const String WENDA_LIST = "wenda/list/";
72 |
73 | //分享文章
74 | static const String SHARE_ARTICLE = "lg/user_article/add/json";
75 |
76 | //分享文章列表
77 | static const String SHARE_ARTICLE_LIST = "user/lg/private_articles/";
78 | }
79 |
--------------------------------------------------------------------------------
/lib/page/system/system_fragment.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:provider/provider.dart';
3 | import 'package:wanandroidflutter/generated/l10n.dart';
4 | import 'package:wanandroidflutter/page/system/knowledge/KnowledgeFragment.dart';
5 | import 'package:wanandroidflutter/page/system/navigation/NavigationFragment.dart';
6 | import 'package:wanandroidflutter/theme/theme_model.dart';
7 |
8 | class SystemFragment extends StatefulWidget {
9 | @override
10 | _SystemFragmentState createState() => _SystemFragmentState();
11 | }
12 |
13 | class _SystemFragmentState extends State
14 | with SingleTickerProviderStateMixin, AutomaticKeepAliveClientMixin {
15 | TabController mTabController;
16 | int index = 0;
17 |
18 | @override
19 | void initState() {
20 | // TODO: implement initState
21 | super.initState();
22 | mTabController =
23 | TabController(initialIndex: 0, length: 2, vsync: this);
24 | }
25 |
26 | @override
27 | Widget build(BuildContext context) {
28 | var appTheme = Provider.of(context);
29 | return Scaffold(
30 | appBar: AppBar(
31 | backgroundColor: appTheme.themeColor,
32 | centerTitle: true,
33 | title: TabBar(
34 | controller: mTabController,
35 | //可以和TabBarView使用同一个TabController
36 | tabs: [
37 | Tab(
38 | text: S.of(context).tab_system,
39 | ),
40 | Tab(
41 | text: S.of(context).tab_navigation,
42 | )
43 | ],
44 | isScrollable: true,
45 | indicatorColor: Colors.white,
46 | labelColor: Colors.white,
47 | labelStyle: Theme.of(context).textTheme.subtitle,
48 | unselectedLabelColor: Colors.grey,
49 | unselectedLabelStyle: Theme.of(context).textTheme.caption,
50 | ),
51 | ),
52 | body: TabBarView(
53 | controller: mTabController,
54 | children: [KnowledgeFragment(), NavigationFragment()],
55 | ),
56 | );
57 | }
58 |
59 | @override
60 | // TODO: implement wantKeepAlive
61 | bool get wantKeepAlive => true;
62 | }
63 |
--------------------------------------------------------------------------------
/lib/page/tabs.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:provider/provider.dart';
3 | import 'package:wanandroidflutter/generated/l10n.dart';
4 | import 'package:wanandroidflutter/page/home/home_fragment.dart';
5 | import 'package:wanandroidflutter/page/project/project_fragment.dart';
6 | import 'package:wanandroidflutter/page/system/system_fragment.dart';
7 | import 'package:wanandroidflutter/page/wechat/wechat_fragment.dart';
8 | import 'package:wanandroidflutter/theme/theme_model.dart';
9 |
10 | class Tabs extends StatefulWidget {
11 | final index;
12 |
13 | Tabs({Key key, this.index = 0}) : super(key: key);
14 |
15 | _TabsState createState() => _TabsState(this.index);
16 | }
17 |
18 | class _TabsState extends State {
19 | int _currentIndex = 0;
20 |
21 | _TabsState(index) {
22 | this._currentIndex = index;
23 | }
24 |
25 | List _pageList = [
26 | HomeFragment(),
27 | SystemFragment(),
28 | WeChatFragment(),
29 | ProjectFragment()
30 | ];
31 |
32 | @override
33 | Widget build(BuildContext context) {
34 | var appTheme = Provider.of(context);
35 | return Scaffold(
36 | body: this._pageList[this._currentIndex],
37 | bottomNavigationBar: BottomNavigationBar(
38 | currentIndex: this._currentIndex,
39 | //配置对应的索引值选中
40 | onTap: (int index) {
41 | setState(() {
42 | //改变状态
43 | this._currentIndex = index;
44 | });
45 | },
46 | fixedColor: appTheme.themeColor,
47 | //选中的颜色
48 | type: BottomNavigationBarType.fixed,
49 | //配置底部tabs可以有多个按钮
50 | items: [
51 | BottomNavigationBarItem(icon: Icon(Icons.home), title: Text(S.of(context).tab_home)),
52 | BottomNavigationBarItem(
53 | icon: Icon(Icons.category), title: Text(S.of(context).tab_system)),
54 | BottomNavigationBarItem(
55 | icon: Icon(Icons.settings), title: Text(S.of(context).tab_wechat)),
56 | BottomNavigationBarItem(
57 | icon: Icon(Icons.category), title: Text(S.of(context).tab_project))
58 | ],
59 | ),
60 | );
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: wanandroidflutter
2 | description: A new Flutter application.
3 |
4 | # The following defines the version and build number for your application.
5 | # A version number is three numbers separated by dots, like 1.2.43
6 | # followed by an optional build number separated by a +.
7 | # Both the version and the builder number may be overridden in flutter
8 | # build by specifying --build-name and --build-number, respectively.
9 | # In Android, build-name is used as versionName while build-number used as versionCode.
10 | # Read more about Android versioning at https://developer.android.com/studio/publish/versioning
11 | # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
12 | # Read more about iOS versioning at
13 | # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
14 | version: 2.0+1
15 |
16 | environment:
17 | sdk: ">=2.1.0 <3.0.0"
18 |
19 | dependencies:
20 | flutter:
21 | sdk: flutter
22 | cupertino_icons: ^0.1.2
23 | flutter_swiper: ^1.1.6
24 | dio: ^3.0.9
25 | cookie_jar: ^1.0.1
26 | dio_cookie_manager: ^1.0.0
27 | flutter_easyrefresh: ^1.2.7
28 | event_bus: ^1.1.1
29 | path_provider: ^0.5.0+1
30 | shared_preferences: ^0.5.6
31 | webview_flutter: ^0.3.19+9
32 | flutter_html: ^0.8.2
33 | image_picker: ^0.6.5+2
34 | image_cropper: ^1.2.1
35 | flare_flutter: ^2.0.2
36 | fluttertoast: ^4.0.0
37 | url_launcher: ^5.4.2
38 | date_format: ^1.0.8
39 | provider: 3.2.0
40 | permission_handler: ^5.0.0+hotfix.3
41 |
42 | dev_dependencies:
43 | flutter_test:
44 | sdk: flutter
45 | flutter_localizations:
46 | sdk: flutter
47 |
48 |
49 |
50 | flutter:
51 | uses-material-design: true
52 |
53 | fonts:
54 | - family: normal
55 | fonts:
56 | - asset: assets/fonts/NotoSans-Regular.ttf
57 | - family: kuaile
58 | fonts:
59 | - asset: assets/fonts/ZCOOLKuaiLe-Regular.ttf
60 |
61 | assets:
62 | - assets/flrs/
63 | - assets/img/ic_back.png
64 | - assets/img/load_fail.png
65 | - assets/img/load_no_data.png
66 | - assets/img/ic_default.png
67 | - assets/img/ic_avatar.png
68 | flutter_intl:
69 | enabled: true
--------------------------------------------------------------------------------
/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
15 |
19 |
26 |
27 |
28 |
29 |
30 |
31 |
33 |
36 |
37 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/lib/widget/favourite_animation.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flare_flutter/flare_actor.dart';
3 |
4 | class FavouriteAnimation extends StatefulWidget {
5 | // hero动画唯一标识
6 | final Object tag;
7 |
8 | final bool isAdded;
9 |
10 | const FavouriteAnimation({Key key, this.tag, this.isAdded}) : super(key: key);
11 |
12 | @override
13 | _FavouriteAnimationState createState() => _FavouriteAnimationState();
14 | }
15 |
16 | class _FavouriteAnimationState extends State {
17 | bool isPlaying = false;
18 |
19 | @override
20 | void initState() {
21 | // TODO: implement initState
22 | super.initState();
23 |
24 | // addPostFrameCallback 是 StatefulWidge 渲染结束的回调,只会被调用一次,
25 | // 之后 StatefulWidget 需要刷新 UI 也不会被调用,addPostFrameCallback 的使用方法是在 initState 里添加回调
26 | WidgetsBinding.instance.addPostFrameCallback((_) {
27 | setState(() {
28 | isPlaying = true;
29 | });
30 | });
31 | }
32 |
33 | @override
34 | Widget build(BuildContext context) {
35 | return Hero(
36 | tag: widget.tag,
37 | child: FlareActor(
38 | "assets/flrs/like.flr",
39 | alignment: Alignment.center,
40 | fit: BoxFit.contain,
41 | animation: widget.isAdded ? 'like' : 'unLike',
42 | shouldClip: false,
43 | isPaused: !isPlaying,
44 | callback: (name) {
45 | Navigator.pop(context);
46 | isPlaying = false;
47 | },
48 | ),
49 | );
50 | }
51 | }
52 |
53 | class HeroDialogRoute extends PageRoute {
54 | final WidgetBuilder builder;
55 |
56 | HeroDialogRoute({this.builder}) : super();
57 |
58 | @override
59 | Color get barrierColor => Colors.black12;
60 |
61 | @override
62 | String get barrierLabel => null;
63 |
64 | // barrierDismissible若为false,点击对话框周围,对话框不会关闭;若为true,点击对话框周围,对话框自动关闭
65 | @override
66 | bool get barrierDismissible => true;
67 |
68 | // 是否透明
69 | @override
70 | bool get opaque => false;
71 |
72 | // 创建转场动画
73 | @override
74 | Widget buildPage(BuildContext context, Animation animation,
75 | Animation secondaryAnimation) {
76 | return builder(context);
77 | }
78 |
79 | @override
80 | bool get maintainState => true;
81 |
82 | // 转场动画持续时间
83 | @override
84 | Duration get transitionDuration => const Duration(milliseconds: 800);
85 | }
86 |
--------------------------------------------------------------------------------
/lib/splash.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 | import 'dart:convert';
3 | import 'package:flutter/material.dart';
4 | import 'package:wanandroidflutter/application.dart';
5 | import 'package:wanandroidflutter/data/login.dart';
6 | import 'package:wanandroidflutter/http/api.dart';
7 | import 'package:wanandroidflutter/http/http_request.dart';
8 | import 'package:wanandroidflutter/utils/Config.dart';
9 | import 'package:wanandroidflutter/utils/login_event.dart';
10 | import 'main_page.dart';
11 |
12 | class SplashView extends StatefulWidget {
13 | @override
14 | _SplashViewState createState() => _SplashViewState();
15 | }
16 |
17 | class _SplashViewState extends State {
18 | getUserInfo() async {
19 | String info = Application.sp.getString(Config.SP_USER_INFO);
20 | if (info != null && info.isNotEmpty) {
21 | Map userMap = json.decode(info);
22 | LoginData userEntity = new LoginData.fromJson(userMap);
23 | String _name = userEntity.username;
24 | String _pwd = Application.sp.getString(Config.SP_PWD);
25 | if (_pwd != null && _pwd.isNotEmpty) {
26 | doLogin(_name, _pwd);
27 | }
28 | }
29 | }
30 |
31 | // 登录
32 | doLogin(String _name, String _pwd) {
33 | var data;
34 | data = {'username': _name, 'password': _pwd};
35 | HttpRequest.getInstance().post(Api.LOGIN, data: data,
36 | successCallBack: (data) {
37 | Application.eventBus.fire(LoginEvent());
38 | saveInfo(data);
39 | Navigator.of(context).pop();
40 | }, errorCallBack: (code, msg) {});
41 | }
42 |
43 | // 保存用户信息
44 | void saveInfo(data) async {
45 | await Application.sp.putString(Config.SP_USER_INFO, data);
46 | }
47 |
48 | void countdown() {
49 | Timer(new Duration(seconds: 2), () {
50 | Navigator.pushAndRemoveUntil(
51 | context,
52 | MaterialPageRoute(builder: (context) => MainPage()),
53 | (route) => route == null,
54 | );
55 | });
56 | }
57 |
58 | @override
59 | void initState() {
60 | super.initState();
61 | getUserInfo();
62 | countdown();
63 | }
64 |
65 | @override
66 | Widget build(BuildContext context) {
67 | return Scaffold(
68 | body: MediaQuery.removePadding(
69 | context: context,
70 | removeTop: true,
71 | child: Container(
72 | alignment: Alignment.center,
73 | child: Icon(Icons.android),
74 | )));
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/lib/widget/custom_refresh.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_easyrefresh/easy_refresh.dart';
3 | import 'package:wanandroidflutter/generated/l10n.dart';
4 |
5 | ///自定义刷新控件头部尾部
6 | class CustomRefresh extends StatefulWidget {
7 | Widget child;
8 | GlobalKey easyRefreshKey;
9 |
10 | Function onRefresh;
11 | Function loadMore;
12 |
13 | CustomRefresh({
14 | @required this.child,
15 | this.onRefresh,
16 | this.loadMore,
17 | @required this.easyRefreshKey,
18 | });
19 |
20 | @override
21 | State createState() {
22 | return _CustomRefreshState();
23 | }
24 | }
25 |
26 | class _CustomRefreshState extends State {
27 | GlobalKey headerKey;
28 | GlobalKey footerKey;
29 |
30 | @override
31 | void initState() {
32 | super.initState();
33 | headerKey = new GlobalKey();
34 |
35 | footerKey = new GlobalKey();
36 | }
37 |
38 | @override
39 | Widget build(BuildContext context) {
40 | return EasyRefresh(
41 | key: widget.easyRefreshKey,
42 | autoLoad: false,
43 | onRefresh: widget.onRefresh,
44 | loadMore: widget.loadMore,
45 | behavior: ScrollOverBehavior(),
46 | refreshHeader: ClassicsHeader(
47 | key: headerKey,
48 | refreshText: S.of(context).down_refresh,
49 | refreshReadyText: S.of(context).release_refresh,
50 | refreshingText: S.of(context).refreshing,
51 | refreshedText: S.of(context).complete_refresh,
52 | moreInfo: S.of(context).update_time + "%T",
53 | bgColor: Colors.transparent,
54 | textColor: Colors.black87,
55 | moreInfoColor: Colors.black54,
56 | isFloat: false,
57 | showMore: true,
58 | ),
59 | refreshFooter: ClassicsFooter(
60 | key: footerKey,
61 | loadText: S.of(context).up_refresh,
62 | loadReadyText: S.of(context).release_refresh,
63 | loadingText: S.of(context).refreshing,
64 | loadedText: S.of(context).complete_refresh,
65 | noMoreText: S.of(context).complete_refresh,
66 | moreInfo: S.of(context).update_time + "%T",
67 | bgColor: Colors.transparent,
68 | textColor: Colors.black87,
69 | moreInfoColor: Colors.black54,
70 | isFloat: false,
71 | showMore: true,
72 | ),
73 | child: widget.child,
74 | );
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/lib/generated/intl/messages_all.dart:
--------------------------------------------------------------------------------
1 | // DO NOT EDIT. This is code generated via package:intl/generate_localized.dart
2 | // This is a library that looks up messages for specific locales by
3 | // delegating to the appropriate library.
4 |
5 | // Ignore issues from commonly used lints in this file.
6 | // ignore_for_file:implementation_imports, file_names, unnecessary_new
7 | // ignore_for_file:unnecessary_brace_in_string_interps, directives_ordering
8 | // ignore_for_file:argument_type_not_assignable, invalid_assignment
9 | // ignore_for_file:prefer_single_quotes, prefer_generic_function_type_aliases
10 | // ignore_for_file:comment_references
11 |
12 | import 'dart:async';
13 |
14 | import 'package:intl/intl.dart';
15 | import 'package:intl/message_lookup_by_library.dart';
16 | import 'package:intl/src/intl_helpers.dart';
17 |
18 | import 'messages_en.dart' as messages_en;
19 | import 'messages_zh.dart' as messages_zh;
20 |
21 | typedef Future LibraryLoader();
22 | Map _deferredLibraries = {
23 | 'en': () => new Future.value(null),
24 | 'zh': () => new Future.value(null),
25 | };
26 |
27 | MessageLookupByLibrary _findExact(String localeName) {
28 | switch (localeName) {
29 | case 'en':
30 | return messages_en.messages;
31 | case 'zh':
32 | return messages_zh.messages;
33 | default:
34 | return null;
35 | }
36 | }
37 |
38 | /// User programs should call this before using [localeName] for messages.
39 | Future initializeMessages(String localeName) async {
40 | var availableLocale = Intl.verifiedLocale(
41 | localeName,
42 | (locale) => _deferredLibraries[locale] != null,
43 | onFailure: (_) => null);
44 | if (availableLocale == null) {
45 | return new Future.value(false);
46 | }
47 | var lib = _deferredLibraries[availableLocale];
48 | await (lib == null ? new Future.value(false) : lib());
49 | initializeInternalMessageLookup(() => new CompositeMessageLookup());
50 | messageLookup.addLocale(availableLocale, _findGeneratedMessagesFor);
51 | return new Future.value(true);
52 | }
53 |
54 | bool _messagesExistFor(String locale) {
55 | try {
56 | return _findExact(locale) != null;
57 | } catch (e) {
58 | return false;
59 | }
60 | }
61 |
62 | MessageLookupByLibrary _findGeneratedMessagesFor(String locale) {
63 | var actualLocale = Intl.verifiedLocale(locale, _messagesExistFor,
64 | onFailure: (_) => null);
65 | if (actualLocale == null) return null;
66 | return _findExact(actualLocale);
67 | }
68 |
--------------------------------------------------------------------------------
/ios/Runner/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/lib/widget/login_widget.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/cupertino.dart';
2 | import 'package:flutter/material.dart';
3 | import 'package:provider/provider.dart';
4 | import 'package:wanandroidflutter/theme/theme_model.dart';
5 |
6 | class LoginTopPanel extends StatelessWidget {
7 | @override
8 | Widget build(BuildContext context) {
9 | var appTheme = Provider.of(context);
10 | return ClipPath(
11 | clipper: BottomClipper(),
12 | child: Container(
13 | height: 200,
14 | color: appTheme.themeColor,
15 | ),
16 | );
17 | }
18 | }
19 |
20 | class LoginLogo extends StatelessWidget {
21 | @override
22 | Widget build(BuildContext context) {
23 | var appTheme = Provider.of(context);
24 | return Hero(
25 | tag: "loginlogo",
26 | child: Image.asset(
27 | "assets/img/ic_avatar.png",
28 | width: 230,
29 | height: 200,
30 | fit: BoxFit.fitWidth,
31 | color: Colors.white,
32 | colorBlendMode: BlendMode.srcIn,
33 | ),
34 | );
35 | }
36 | }
37 |
38 | class LoginFormContainer extends StatelessWidget {
39 | final Widget child;
40 |
41 | LoginFormContainer({this.child});
42 |
43 | @override
44 | Widget build(BuildContext context) {
45 | var appTheme = Provider.of(context);
46 | return Container(
47 | margin: EdgeInsets.symmetric(horizontal: 30, vertical: 30),
48 | padding: EdgeInsets.symmetric(horizontal: 10, vertical: 10),
49 | decoration: ShapeDecoration(
50 | shape: RoundedRectangleBorder(),
51 | color: appTheme.themeColor,
52 | shadows: [
53 | BoxShadow(
54 | color: appTheme.themeColor.withAlpha(20),
55 | offset: Offset(1.0, 1.0),
56 | blurRadius: 10.0,
57 | spreadRadius: 3.0,
58 | )
59 | ]),
60 | child: child,
61 | );
62 | }
63 | }
64 |
65 | class BottomClipper extends CustomClipper {
66 | @override
67 | getClip(Size size) {
68 | var path = Path();
69 | path.lineTo(0, 0);
70 | path.lineTo(0, size.height - 50);
71 |
72 | var p1 = Offset(size.width / 2, size.height);
73 | var p2 = Offset(size.width, size.height - 50);
74 | path.quadraticBezierTo(p1.dx, p1.dy, p2.dx, p2.dy);
75 | path.lineTo(size.width, size.height - 50);
76 | path.lineTo(size.width, 0);
77 | return path;
78 | }
79 |
80 | @override
81 | bool shouldReclip(CustomClipper oldClipper) {
82 | return false;
83 | }
84 | }
--------------------------------------------------------------------------------
/lib/widget/coin_item.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:provider/provider.dart';
3 | import 'package:wanandroidflutter/data/rank.dart';
4 | import 'package:wanandroidflutter/theme/dark_model.dart';
5 | import 'package:wanandroidflutter/theme/theme_model.dart';
6 |
7 | class CoinRankWidget extends StatefulWidget {
8 | RankData rankData;
9 |
10 | CoinRankWidget(this.rankData);
11 |
12 | @override
13 | _CoinRankWidgetState createState() => _CoinRankWidgetState();
14 | }
15 |
16 | class _CoinRankWidgetState extends State {
17 | RankData rankData;
18 |
19 | @override
20 | Widget build(BuildContext context) {
21 | var appTheme = Provider.of(context);
22 | var isDarkMode = Provider.of(context).isDark;
23 | rankData = widget.rankData;
24 | return ListTile(
25 | dense: true,
26 | contentPadding: EdgeInsets.symmetric(horizontal: 25, vertical: 10),
27 | onTap: () {},
28 | leading: buildIcon(rankData, isDarkMode),
29 | title: Text(
30 | rankData == null ? "--" : (rankData.username),
31 | style: TextStyle(fontSize: 16),
32 | ),
33 | trailing: Text(
34 | rankData == null ? "--" : rankData.coinCount.toString(),
35 | style: TextStyle(
36 | color: !isDarkMode ? Colors.black : Colors.white, fontSize: 15.0, fontWeight: FontWeight.bold),
37 | ),
38 | );
39 | }
40 |
41 | Widget buildIcon(RankData rankData, bool isDarkMode) {
42 | if (rankData == null) {
43 | return Padding(
44 | padding: EdgeInsets.only(left: 8),
45 | child: Text(
46 | "--",
47 | style: TextStyle(
48 | color: !isDarkMode ? Colors.black : Colors.white.withAlpha(120), fontSize: 15.0, fontWeight: FontWeight.bold),
49 | ),
50 | );
51 | }
52 | if (rankData.rank == 1 || rankData.rank == 2 || rankData.rank == 3) {
53 | switch (rankData.rank) {
54 | case 1:
55 | return Icon(Icons.looks_one, color: Colors.yellow);
56 | break;
57 | case 2:
58 | return Icon(Icons.looks_two, color: Colors.blueGrey);
59 | break;
60 | case 3:
61 | return Icon(Icons.looks_3, color: Colors.orangeAccent);
62 | break;
63 | }
64 | } else {
65 | return Padding(
66 | padding: EdgeInsets.only(left: 8),
67 | child: Text(
68 | rankData.rank.toString(),
69 | style: TextStyle(
70 | color: Colors.black, fontSize: 15.0, fontWeight: FontWeight.bold),
71 | ),
72 | );
73 | }
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/lib/widget/marquee_widget.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 |
3 | import 'package:flutter/material.dart';
4 |
5 | //跑马灯效果 参考 https://github.com/baoolong/MarqueeWidget/blob/master/lib/marquee_flutter.dart
6 | class MarqueeWidget extends StatefulWidget {
7 | var width = 200.0;
8 | var height = 20.0;
9 | TextStyle style;
10 | String text;
11 |
12 | MarqueeWidget({this.width, this.height, @required this.text, style})
13 | : style = style == null ? TextStyle(fontSize: 16) : style;
14 |
15 | @override
16 | State createState() {
17 | return _MarqueeWidgetState();
18 | }
19 | }
20 |
21 | class _MarqueeWidgetState extends State {
22 | var _controller = ScrollController();
23 | double position = 0.0;
24 | Timer timer;
25 |
26 | @override
27 | void initState() {
28 | super.initState();
29 | if (getCenterWidgetWidth() > widget.width) {
30 | timer = Timer.periodic(Duration(milliseconds: 100), (timer) {
31 | double pixels = _controller.position.pixels;
32 | double maxScrollExtent = _controller.position.maxScrollExtent;
33 | if (pixels + 3.0 >= maxScrollExtent) {
34 | position = 0;
35 | _controller.jumpTo(position);
36 | }
37 | position += 3.0;
38 | _controller.animateTo(position,
39 | duration: new Duration(milliseconds: 90), curve: Curves.linear);
40 | });
41 | }
42 | }
43 |
44 | Widget getStartEndWidget() {
45 | return Container(
46 | width: widget.width,
47 | );
48 | }
49 |
50 | Widget getCenterWidget() {
51 | return Align(
52 | alignment: Alignment.center,
53 | child: Text(
54 | widget.text,
55 | maxLines: 1,
56 | textAlign: TextAlign.center,
57 | style: widget.style,
58 | ),
59 | );
60 | }
61 |
62 | double getCenterWidgetWidth() {
63 | return widget.text.length * widget.style.fontSize;
64 | }
65 |
66 | @override
67 | Widget build(BuildContext context) {
68 | return Center(
69 | child: getCenterWidgetWidth() > widget.width
70 | ? ListView(
71 | physics: new NeverScrollableScrollPhysics(),
72 | scrollDirection: Axis.horizontal,
73 | controller: _controller,
74 | children: [
75 | getStartEndWidget(),
76 | getCenterWidget(),
77 | getStartEndWidget(),
78 | ],
79 | )
80 | : Text(
81 | widget.text,
82 | maxLines: 1,
83 | style: widget.style,
84 | ),
85 | );
86 | }
87 |
88 | @override
89 | void dispose() {
90 | super.dispose();
91 | if (timer != null) {
92 | timer.cancel();
93 | }
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/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 |
25 |
26 | apply plugin: 'com.android.application'
27 | apply plugin: 'kotlin-android'
28 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
29 |
30 | def keystorePropertiesFile = rootProject.file("key.properties")
31 | def keystoreProperties = new Properties()
32 | keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
33 |
34 | android {
35 | compileSdkVersion 28
36 |
37 | sourceSets {
38 | main.java.srcDirs += 'src/main/kotlin'
39 | }
40 |
41 | lintOptions {
42 | disable 'InvalidPackage'
43 | }
44 |
45 | defaultConfig {
46 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
47 | applicationId "com.example.wanandroidflutter"
48 | minSdkVersion 16
49 | targetSdkVersion 28
50 | versionCode flutterVersionCode.toInteger()
51 | versionName flutterVersionName
52 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
53 | }
54 |
55 | signingConfigs {
56 | release {
57 | keyAlias keystoreProperties['keyAlias']
58 | keyPassword keystoreProperties['keyPassword']
59 | storeFile file(keystoreProperties['storeFile'])
60 | storePassword keystoreProperties['storePassword']
61 | }
62 | }
63 |
64 | buildTypes {
65 | release {
66 | // TODO: Add your own signing config for the release build.
67 | // Signing with the debug keys for now, so `flutter run --release` works.
68 | signingConfig signingConfigs.release
69 | }
70 | }
71 | }
72 |
73 | flutter {
74 | source '../..'
75 | }
76 |
77 | dependencies {
78 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
79 | testImplementation 'junit:junit:4.12'
80 | androidTestImplementation 'androidx.test:runner:1.1.1'
81 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
82 | }
83 |
--------------------------------------------------------------------------------
/lib/widget/title_bar.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:provider/provider.dart';
3 | import 'package:wanandroidflutter/theme/theme_model.dart';
4 |
5 | import 'marquee_widget.dart';
6 |
7 | class TitleBar extends StatefulWidget implements PreferredSizeWidget {
8 | Widget leftButton;
9 | String title = "";
10 | bool isShowBack = true;
11 |
12 | TitleBar({this.leftButton, this.isShowBack, this.title});
13 |
14 | @override
15 | _TitleBarState createState() => _TitleBarState();
16 |
17 | static Widget textButton(
18 | String text, {
19 | Color color = Colors.white,
20 | Function() press,
21 | }) {
22 | return InkWell(
23 | child: Container(
24 | margin: EdgeInsets.only(right: 5),
25 | padding: EdgeInsets.all(5),
26 | child: Text(
27 | text,
28 | style: TextStyle(color: color),
29 | ),
30 | ),
31 | onTap: press,
32 | );
33 | }
34 |
35 | static Widget iconButton(
36 | {IconData icon, Color color = Colors.white, Function() press}) {
37 | return IconButton(
38 | padding: EdgeInsets.all(2),
39 | icon: Icon(
40 | icon,
41 | color: color,
42 | ),
43 | onPressed: press);
44 | }
45 |
46 | @override
47 | Size get preferredSize => Size.fromHeight(56.0);
48 | }
49 |
50 | class _TitleBarState extends State {
51 | @override
52 | Widget build(BuildContext context) {
53 | var appTheme = Provider.of(context);
54 | return Container(
55 | padding: EdgeInsets.only(top: MediaQuery.of(context).padding.top),
56 | decoration: BoxDecoration(
57 | gradient: LinearGradient(
58 | begin: Alignment.centerLeft,
59 | end: Alignment.centerRight,
60 | colors: [appTheme.themeColor.withAlpha(180), appTheme.themeColor])),
61 | child: Stack(
62 | children: [
63 | Offstage(
64 | offstage: !widget.isShowBack,
65 | child: Container(
66 | alignment: Alignment.centerLeft,
67 | child: widget.leftButton == null
68 | ? IconButton(
69 | icon: Icon(
70 | Icons.arrow_back,
71 | color: Colors.white,
72 | ),
73 | onPressed: () {
74 | Navigator.of(context).pop();
75 | },
76 | )
77 | : widget.leftButton,
78 | ),
79 | ),
80 | Container(
81 | margin: EdgeInsets.symmetric(horizontal: 50),
82 | child: Center(
83 | child: MarqueeWidget(
84 | text: widget.title,
85 | height: 56,
86 | width: MediaQuery.of(context).size.width - 90,
87 | style: TextStyle(fontSize: 18, color: Colors.white),
88 | ),
89 | ),
90 | ),
91 | ],
92 | ),
93 | );
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "size" : "20x20",
5 | "idiom" : "iphone",
6 | "filename" : "Icon-App-20x20@2x.png",
7 | "scale" : "2x"
8 | },
9 | {
10 | "size" : "20x20",
11 | "idiom" : "iphone",
12 | "filename" : "Icon-App-20x20@3x.png",
13 | "scale" : "3x"
14 | },
15 | {
16 | "size" : "29x29",
17 | "idiom" : "iphone",
18 | "filename" : "Icon-App-29x29@1x.png",
19 | "scale" : "1x"
20 | },
21 | {
22 | "size" : "29x29",
23 | "idiom" : "iphone",
24 | "filename" : "Icon-App-29x29@2x.png",
25 | "scale" : "2x"
26 | },
27 | {
28 | "size" : "29x29",
29 | "idiom" : "iphone",
30 | "filename" : "Icon-App-29x29@3x.png",
31 | "scale" : "3x"
32 | },
33 | {
34 | "size" : "40x40",
35 | "idiom" : "iphone",
36 | "filename" : "Icon-App-40x40@2x.png",
37 | "scale" : "2x"
38 | },
39 | {
40 | "size" : "40x40",
41 | "idiom" : "iphone",
42 | "filename" : "Icon-App-40x40@3x.png",
43 | "scale" : "3x"
44 | },
45 | {
46 | "size" : "60x60",
47 | "idiom" : "iphone",
48 | "filename" : "Icon-App-60x60@2x.png",
49 | "scale" : "2x"
50 | },
51 | {
52 | "size" : "60x60",
53 | "idiom" : "iphone",
54 | "filename" : "Icon-App-60x60@3x.png",
55 | "scale" : "3x"
56 | },
57 | {
58 | "size" : "20x20",
59 | "idiom" : "ipad",
60 | "filename" : "Icon-App-20x20@1x.png",
61 | "scale" : "1x"
62 | },
63 | {
64 | "size" : "20x20",
65 | "idiom" : "ipad",
66 | "filename" : "Icon-App-20x20@2x.png",
67 | "scale" : "2x"
68 | },
69 | {
70 | "size" : "29x29",
71 | "idiom" : "ipad",
72 | "filename" : "Icon-App-29x29@1x.png",
73 | "scale" : "1x"
74 | },
75 | {
76 | "size" : "29x29",
77 | "idiom" : "ipad",
78 | "filename" : "Icon-App-29x29@2x.png",
79 | "scale" : "2x"
80 | },
81 | {
82 | "size" : "40x40",
83 | "idiom" : "ipad",
84 | "filename" : "Icon-App-40x40@1x.png",
85 | "scale" : "1x"
86 | },
87 | {
88 | "size" : "40x40",
89 | "idiom" : "ipad",
90 | "filename" : "Icon-App-40x40@2x.png",
91 | "scale" : "2x"
92 | },
93 | {
94 | "size" : "76x76",
95 | "idiom" : "ipad",
96 | "filename" : "Icon-App-76x76@1x.png",
97 | "scale" : "1x"
98 | },
99 | {
100 | "size" : "76x76",
101 | "idiom" : "ipad",
102 | "filename" : "Icon-App-76x76@2x.png",
103 | "scale" : "2x"
104 | },
105 | {
106 | "size" : "83.5x83.5",
107 | "idiom" : "ipad",
108 | "filename" : "Icon-App-83.5x83.5@2x.png",
109 | "scale" : "2x"
110 | },
111 | {
112 | "size" : "1024x1024",
113 | "idiom" : "ios-marketing",
114 | "filename" : "Icon-App-1024x1024@1x.png",
115 | "scale" : "1x"
116 | }
117 | ],
118 | "info" : {
119 | "version" : 1,
120 | "author" : "xcode"
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/lib/main_page.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:provider/provider.dart';
3 | import 'package:wanandroidflutter/application.dart';
4 | import 'package:wanandroidflutter/constant/Constants.dart';
5 | import 'package:wanandroidflutter/generated/l10n.dart';
6 | import 'package:wanandroidflutter/theme/font_model.dart';
7 | import 'package:wanandroidflutter/theme/locale_model.dart';
8 | import 'package:wanandroidflutter/theme/theme_colors.dart';
9 | import 'package:wanandroidflutter/page/tabs.dart';
10 | import 'package:wanandroidflutter/routes/routes.dart';
11 | import 'package:wanandroidflutter/theme/dark_model.dart';
12 | import 'package:wanandroidflutter/theme/theme_model.dart';
13 | import 'package:wanandroidflutter/utils/Config.dart';
14 | import 'package:flutter_localizations/flutter_localizations.dart';
15 |
16 |
17 | class MainPage extends StatefulWidget {
18 | @override
19 | _MainPageState createState() => _MainPageState();
20 | }
21 |
22 | class _MainPageState extends State {
23 | @override
24 | void initState() {
25 | super.initState();
26 | queryThemeColor().then((index) {
27 | Provider.of(context).updateThemeColor(getThemeColors()[index]);
28 | });
29 | queryDark().then((value) {
30 | Provider.of(context).updateDarkMode(value);
31 | if (value) {
32 | Provider.of(context).updateThemeColor(Color(0xff323638));
33 | }
34 | });
35 | queryFontIndex().then((index) {
36 | Provider.of(context).updateFontIndex(index);
37 | });
38 | }
39 |
40 | queryThemeColor() async {
41 | int themeColorIndex = Application.sp.getInt(Config.SP_THEME_COLOR) ?? 0;
42 | return themeColorIndex;
43 | }
44 |
45 | /// 查询暗黑模式
46 | queryDark() async {
47 | bool isDark = Application.sp.getBool(Config.SP_DARK_MODEL) ?? false;
48 | return isDark;
49 | }
50 |
51 | /// 查询字体模式
52 | queryFontIndex() async {
53 | int fontIndex = Application.sp.getInt(Config.SP_FONT_INDEX) ?? 0;
54 | return fontIndex;
55 | }
56 |
57 |
58 | @override
59 | Widget build(BuildContext context) {
60 | var appTheme = Provider.of(context);
61 | var darkMode = Provider.of(context);
62 | var fontMode = Provider.of(context);
63 | var localeMode = Provider.of(context);
64 | return MaterialApp(
65 | theme: getTheme(appTheme.themeColor, isDarkMode: darkMode.isDark, fontIndex: fontMode.fontIndex),
66 | locale: localeMode.locale,
67 | localizationsDelegates: const [
68 | S.delegate,
69 | GlobalMaterialLocalizations.delegate,
70 | GlobalCupertinoLocalizations.delegate,
71 | GlobalWidgetsLocalizations.delegate
72 | ],
73 | // 设置中文为首选项
74 | supportedLocales: [const Locale('zh', ''), ...S.delegate.supportedLocales],
75 | home: Tabs(),
76 | initialRoute: '/',
77 | onGenerateRoute: onGenerateRoute,
78 | );
79 | }
80 |
81 | getTheme(Color themeColor, {bool isDarkMode = false, int fontIndex = 0}) {
82 | return ThemeData(
83 | fontFamily: Constants.FontList[fontIndex],
84 | brightness: isDarkMode ? Brightness.dark : Brightness.light,
85 | );
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/lib/utils/sp_util.dart:
--------------------------------------------------------------------------------
1 | import 'package:shared_preferences/shared_preferences.dart';
2 |
3 | class SpUtil {
4 | static SpUtil _instance;
5 |
6 | static Future get instance async {
7 | return await getInstance();
8 | }
9 |
10 | static SharedPreferences _sp;
11 |
12 | SpUtil._internal();
13 |
14 | Future _init() async {
15 | _sp = await SharedPreferences.getInstance();
16 | }
17 |
18 | static Future getInstance() async {
19 | if (_instance == null) {
20 | _instance = SpUtil._internal();
21 | }
22 | if (_sp == null) {
23 | await _instance._init();
24 | }
25 | return _instance;
26 | }
27 |
28 | static bool _beforeCheck() {
29 | if (_sp == null) {
30 | return true;
31 | }
32 | return false;
33 | }
34 |
35 | bool hasKey(String key) {
36 | Set keys = getKeys();
37 | return keys.contains(key);
38 | }
39 |
40 | Set getKeys() {
41 | if (_beforeCheck()) {
42 | return null;
43 | }
44 | return _sp.getKeys();
45 | }
46 |
47 | get(String key) {
48 | return _sp.get(key);
49 | }
50 |
51 | getString(String key) {
52 | if (_beforeCheck()) {
53 | return null;
54 | }
55 | return _sp.getString(key);
56 | }
57 |
58 | Future putString(String key, String value) async {
59 | if (_beforeCheck()) {
60 | return null;
61 | }
62 | return _sp.setString(key, value);
63 | }
64 |
65 | getInt(String key) {
66 | if (_beforeCheck()) {
67 | return null;
68 | }
69 | return _sp.getInt(key);
70 | }
71 |
72 | Future putInt(String key, int value) async {
73 | if (_beforeCheck()) {
74 | return null;
75 | }
76 | return _sp.setInt(key, value);
77 | }
78 |
79 | bool getBool(String key) {
80 | if (_beforeCheck()) {
81 | return null;
82 | }
83 | return _sp.getBool(key);
84 | }
85 |
86 | Future putBool(String key, bool value) {
87 | if (_beforeCheck()) {
88 | return null;
89 | }
90 | return _sp.setBool(key, value);
91 | }
92 |
93 | double getDouble(String key) {
94 | if (_beforeCheck()) {
95 | return null;
96 | }
97 | return _sp.getDouble(key);
98 | }
99 |
100 | Future putDouble(String key, double value) {
101 | if (_beforeCheck()) {
102 | return null;
103 | }
104 | return _sp.setDouble(key, value);
105 | }
106 |
107 | List getStringList(String key) {
108 | return _sp.getStringList(key);
109 | }
110 |
111 | Future putStringList(String key, List value) {
112 | if (_beforeCheck()) {
113 | return null;
114 | }
115 | return _sp.setStringList(key, value);
116 | }
117 |
118 | dynamic getDynamic(String key) {
119 | if (_beforeCheck()) {
120 | return null;
121 | }
122 | return _sp.get(key);
123 | }
124 |
125 | Future remove(String key) {
126 | if (_beforeCheck()) {
127 | return null;
128 | }
129 | return _sp.remove(key);
130 | }
131 |
132 | Future clear() {
133 | if (_beforeCheck()) {
134 | return null;
135 | }
136 | return _sp.clear();
137 | }
138 | }
139 |
--------------------------------------------------------------------------------
/lib/utils/common.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 | import 'dart:io';
3 | import 'dart:math';
4 |
5 | import 'package:flutter/cupertino.dart';
6 | import 'package:flutter/material.dart';
7 | import 'package:fluttertoast/fluttertoast.dart';
8 | import 'package:path_provider/path_provider.dart';
9 |
10 | class CommonUtils {
11 | //Dialog 封装
12 | static void showAlertDialog(BuildContext context, String contentText,
13 | {Function confirmCallback,
14 | Function dismissCallback,
15 | String cancelText = "",
16 | String confirmText = ""}) async {
17 | return showDialog(
18 | context: context,
19 | barrierDismissible: true, // user must tap button!
20 | builder: (BuildContext context) {
21 | return AlertDialog(
22 | content: Text(contentText),
23 | actions: [
24 | FlatButton(
25 | child: Text(cancelText),
26 | onPressed: () {
27 | if (dismissCallback != null) {
28 | dismissCallback();
29 | }
30 | Navigator.of(context).pop();
31 | },
32 | ),
33 | FlatButton(
34 | child: Text(confirmText == "" ? '注销' : confirmText),
35 | onPressed: () {
36 | if (confirmCallback != null) {
37 | confirmCallback();
38 | }
39 | Navigator.of(context).pop();
40 | },
41 | )
42 | ],
43 | elevation: 20, //阴影
44 | );
45 | },
46 | );
47 | }
48 |
49 | //清除缓存
50 | static void clearCache() async {
51 | Directory tempDir = await getTemporaryDirectory();
52 | //删除缓存目录
53 | await delDir(tempDir);
54 | //清除图片缓存
55 | PaintingBinding.instance.imageCache.clear();
56 | //await loadCache();
57 | toast("清除缓存成功");
58 | }
59 |
60 | ///递归方式删除目录
61 | static Future delDir(FileSystemEntity file) async {
62 | if (file is Directory) {
63 | final List children = file.listSync();
64 | for (final FileSystemEntity child in children) {
65 | await delDir(child);
66 | }
67 | }
68 | await file.delete();
69 | }
70 |
71 | static toast(String msg) {
72 | Fluttertoast.showToast(msg: msg, fontSize: 14);
73 | }
74 |
75 | static Color getRandomColor() {
76 | Random random = Random();
77 | var temp = random.nextInt(6);
78 | List colors = [
79 | Colors.blueAccent,
80 | Colors.grey,
81 | Colors.redAccent,
82 | Colors.purpleAccent,
83 | Colors.lightGreen,
84 | Colors.deepOrangeAccent,
85 | ];
86 | return colors[temp];
87 | }
88 |
89 | static Future push(BuildContext context, Widget widget) {
90 | Future result = Navigator.push(
91 | context,
92 | // PageRouteBuilder(
93 | // transitionDuration: Duration(milliseconds: 500),
94 | // pageBuilder: (BuildContext context, Animation animation,
95 | // Animation secondaryAnimatioZn) {
96 | // return new FadeTransition(
97 | // //使用渐隐渐入过渡,
98 | // opacity: animation,
99 | // child: widget, //路由B
100 | // );
101 | // },
102 | // ));
103 | // 使用ios的转场动画
104 | CupertinoPageRoute(
105 | builder: (context) => widget,
106 | ));
107 | }
108 | }
109 |
--------------------------------------------------------------------------------
/lib/main.dart:
--------------------------------------------------------------------------------
1 | import 'dart:io';
2 | import 'package:event_bus/event_bus.dart';
3 | import 'package:flutter/material.dart';
4 | import 'package:flutter/services.dart';
5 | import 'package:fluttertoast/fluttertoast.dart';
6 | import 'package:permission_handler/permission_handler.dart';
7 | import 'package:provider/provider.dart';
8 | import 'package:wanandroidflutter/application.dart';
9 | import 'package:wanandroidflutter/splash.dart';
10 | import 'package:wanandroidflutter/theme/dark_model.dart';
11 | import 'package:wanandroidflutter/theme/font_model.dart';
12 | import 'package:wanandroidflutter/theme/locale_model.dart';
13 | import 'package:wanandroidflutter/theme/theme_model.dart';
14 | import 'package:wanandroidflutter/utils/sp_util.dart';
15 |
16 | void main() async {
17 | WidgetsFlutterBinding.ensureInitialized();
18 | Application.sp = await SpUtil.getInstance();
19 | final appTheme = ThemeModel();
20 | final darkMode = DarkMode();
21 | final fontMode = FontModel();
22 | final localeMode = LocaleModel();
23 | if (Platform.isAndroid) {
24 | SystemUiOverlayStyle systemUiOverlayStyle =
25 | SystemUiOverlayStyle(statusBarColor: Colors.transparent);
26 | SystemChrome.setSystemUIOverlayStyle(systemUiOverlayStyle);
27 | }
28 |
29 | // Permission check
30 | Future getPermission() async {
31 | //请求权限
32 | Map statuses = await [
33 | Permission.notification,
34 | Permission.storage,
35 | ].request();
36 | //校验权限
37 | if (statuses[Permission.notification] != PermissionStatus.granted) {
38 | Fluttertoast.showToast(
39 | msg: "需要打开通知权限",
40 | toastLength: Toast.LENGTH_SHORT,
41 | gravity: ToastGravity.CENTER,
42 | timeInSecForIosWeb: 1,
43 | backgroundColor: Colors.white,
44 | textColor: Colors.black,
45 | fontSize: 16.0);
46 | openAppSettings();
47 | }
48 | if (statuses[Permission.storage] != PermissionStatus.granted) {
49 | Fluttertoast.showToast(
50 | msg: "需要打开允许访问存储权限",
51 | toastLength: Toast.LENGTH_SHORT,
52 | gravity: ToastGravity.CENTER,
53 | timeInSecForIosWeb: 1,
54 | backgroundColor: Colors.white,
55 | textColor: Colors.black,
56 | fontSize: 16.0);
57 | openAppSettings();
58 | }
59 | }
60 |
61 | Future.wait([getPermission()]).then((result) {
62 | runApp(
63 | MultiProvider(
64 | // 接受监听
65 | providers: [
66 | ChangeNotifierProvider.value(value: appTheme),
67 | ChangeNotifierProvider.value(value: darkMode),
68 | ChangeNotifierProvider.value(value: fontMode),
69 | ChangeNotifierProvider.value(value: localeMode),
70 | ],
71 | child: MyApp(),
72 | ),
73 | );
74 | });
75 | }
76 |
77 | class MyApp extends StatefulWidget {
78 | @override
79 | _MyAppState createState() => _MyAppState();
80 | }
81 |
82 | class _MyAppState extends State {
83 | @override
84 | void initState() {
85 | // TODO: implement initState
86 | super.initState();
87 | Application.eventBus = EventBus();
88 | }
89 |
90 | @override
91 | void dispose() {
92 | // TODO: implement dispose
93 | super.dispose();
94 | Application.eventBus.destroy();
95 | }
96 |
97 | @override
98 | Widget build(BuildContext context) {
99 | return MaterialApp(
100 | debugShowCheckedModeBanner: true,
101 | home: SplashView(),
102 | );
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
39 |
40 |
41 |
42 |
43 |
44 |
54 |
56 |
62 |
63 |
64 |
65 |
66 |
67 |
73 |
75 |
81 |
82 |
83 |
84 |
86 |
87 |
90 |
91 |
92 |
--------------------------------------------------------------------------------
/lib/widget/page_widget.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:provider/provider.dart';
3 | import 'package:wanandroidflutter/generated/l10n.dart';
4 | import 'package:wanandroidflutter/theme/theme_model.dart';
5 |
6 | import 'load_fail_widget.dart';
7 |
8 | typedef ReloadData = Function();
9 |
10 | //页面加载状态
11 | class PageWidget extends StatefulWidget {
12 | Widget child;
13 | PageStateController controller;
14 | ReloadData reload;
15 | int index = 2;
16 |
17 | PageWidget({this.child, controller, this.reload, this.index = 2})
18 | : controller = controller != null ? controller : PageStateController();
19 |
20 | @override
21 | State createState() {
22 | return _PageWidgetState();
23 | }
24 | }
25 |
26 | class _PageWidgetState extends State {
27 | int index;
28 | VoidCallback _listener;
29 |
30 | @override
31 | void initState() {
32 | super.initState();
33 | index = widget.index;
34 | _listener = () {
35 | setState(() {
36 | switch (widget.controller._state) {
37 | case PageState.Loading:
38 | index = 2;
39 | break;
40 | case PageState.LoadSuccess:
41 | index = 0;
42 | break;
43 | case PageState.LoadFail:
44 | index = 1;
45 | break;
46 | case PageState.NoData:
47 | index = 3;
48 | break;
49 | default:
50 | index = 2;
51 | break;
52 | }
53 | });
54 | };
55 | widget.controller.loadingNotifier.addListener(_listener);
56 | }
57 |
58 | @override
59 | Widget build(BuildContext context) {
60 | var appTheme = Provider.of(context);
61 | return IndexedStack(
62 | index: index,
63 | children: [
64 | widget.child,
65 | LoadFailWidget(
66 | onTap: () {
67 | widget.controller.changeState(PageState.Loading);
68 | widget.reload();
69 | },
70 | ),
71 | Center(
72 | child: CircularProgressIndicator(
73 | valueColor: AlwaysStoppedAnimation(appTheme.themeColor),
74 | ),
75 | ),
76 | GestureDetector(
77 | onTap: () {
78 | widget.controller.changeState(PageState.Loading);
79 | widget.reload();
80 | },
81 | child: noDataWidget(),
82 | )
83 | ],
84 | );
85 | }
86 |
87 | Widget noDataWidget() {
88 | return Center(
89 | child: Column(
90 | mainAxisAlignment: MainAxisAlignment.center,
91 | children: [
92 | ImageIcon(
93 | AssetImage("assets/img/load_no_data.png"),
94 | size: 50,
95 | ),
96 | SizedBox(
97 | height: 10,
98 | ),
99 | Text(
100 | S.of(context).no_data,
101 | style: Theme.of(context).textTheme.caption,
102 | ),
103 | ],
104 | ),
105 | );
106 | }
107 |
108 | @override
109 | void dispose() {
110 | widget.controller.loadingNotifier.removeListener(_listener);
111 | super.dispose();
112 | }
113 | }
114 |
115 | class PageStateController {
116 | //属性监听,也可以用InheritedWidget、Redux实现
117 | ValueNotifier loadingNotifier = ValueNotifier(PageState.Loading);
118 | PageState _state = PageState.Loading;
119 |
120 | void changeState(PageState state) {
121 | this._state = state;
122 | loadingNotifier.value = state;
123 | }
124 | }
125 |
126 | enum PageState { Loading, LoadSuccess, LoadFail, NoData }
127 |
--------------------------------------------------------------------------------
/lib/page/wechat/wechat_fragment.dart:
--------------------------------------------------------------------------------
1 | import 'dart:convert';
2 |
3 | import 'package:flutter/material.dart';
4 | import 'package:provider/provider.dart';
5 | import 'package:wanandroidflutter/data/wechat_tab.dart';
6 | import 'package:wanandroidflutter/http/http_request.dart';
7 | import 'package:wanandroidflutter/http/api.dart';
8 | import 'package:wanandroidflutter/page/wechat/wechat_list_fragment.dart';
9 | import 'package:wanandroidflutter/theme/theme_model.dart';
10 | import 'package:wanandroidflutter/widget/load_fail_widget.dart';
11 |
12 | class WeChatFragment extends StatefulWidget {
13 | @override
14 | WeChatFragmentState createState() => WeChatFragmentState();
15 | }
16 |
17 | class WeChatFragmentState extends State
18 | with SingleTickerProviderStateMixin, AutomaticKeepAliveClientMixin {
19 | TabController mTabController;
20 | List mTabDatas = [];
21 | int mCurrentIndex = 0;
22 | var mPageController = new PageController(initialPage: 0);
23 | var isPageChanged = true;
24 | bool isNetWorkError = false;
25 |
26 | @override
27 | void initState() {
28 | super.initState();
29 | loadWeChatTab();
30 | }
31 |
32 | void loadWeChatTab() async {
33 | HttpRequest.getInstance().get(Api.WECHAT_TAB, successCallBack: (data) {
34 | if (data != null) {
35 | List responseJson = json.decode(data);
36 | setState(() {
37 | isNetWorkError = false;
38 | mTabDatas = responseJson.map((m) => WeChatTab.fromJson(m)).toList();
39 | mTabController = TabController(
40 | initialIndex: 0, length: mTabDatas.length, vsync: this);
41 | });
42 | } else {
43 | setState(() {
44 | isNetWorkError = true;
45 | });
46 | }
47 | }, errorCallBack: (code, msg) {});
48 | }
49 |
50 | List initTabs() {
51 | List temps = [];
52 | for (var i = 0; i < mTabDatas.length; i++) {
53 | temps.add(Tab(
54 | text: mTabDatas[i].name,
55 | ));
56 | }
57 | return temps;
58 | }
59 |
60 | initPages() {
61 | List pages = List();
62 | for (WeChatTab item in mTabDatas) {
63 | var page = WeChatListFragment(item.id);
64 | pages.add(page);
65 | }
66 | return pages;
67 | }
68 |
69 | @override
70 | Widget build(BuildContext context) {
71 | var appTheme = Provider.of(context);
72 | return DefaultTabController(
73 | length: mTabDatas.length,
74 | child: isNetWorkError
75 | ? Scaffold(
76 | body: LoadFailWidget(
77 | onTap: () {
78 | loadWeChatTab();
79 | },
80 | ),
81 | )
82 | : Scaffold(
83 | appBar: AppBar(
84 | backgroundColor: appTheme.themeColor,
85 | title: TabBar(
86 | controller: mTabController,
87 | tabs: initTabs(),
88 | isScrollable: true,
89 | indicatorColor: Colors.white,
90 | labelColor: Colors.white,
91 | labelStyle: Theme.of(context).textTheme.subtitle,
92 | unselectedLabelColor: Colors.grey,
93 | unselectedLabelStyle: Theme.of(context).textTheme.caption,
94 | ),
95 | ),
96 | body: TabBarView(
97 | controller: mTabController,
98 | children: initPages(),
99 | ),
100 | ),
101 | );
102 | }
103 |
104 | @override
105 | // TODO: implement wantKeepAlive
106 | bool get wantKeepAlive => true;
107 | }
108 |
--------------------------------------------------------------------------------
/lib/page/project/project_fragment.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:provider/provider.dart';
3 | import 'dart:convert';
4 | import 'package:wanandroidflutter/data/project_tab.dart';
5 | import 'package:wanandroidflutter/http/http_request.dart';
6 | import 'package:wanandroidflutter/http/api.dart';
7 | import 'package:wanandroidflutter/page/project/project_list_fragment.dart';
8 | import 'package:wanandroidflutter/theme/theme_model.dart';
9 | import 'package:wanandroidflutter/widget/load_fail_widget.dart';
10 |
11 | class ProjectFragment extends StatefulWidget {
12 | @override
13 | ProjectFragmentState createState() => ProjectFragmentState();
14 | }
15 |
16 | class ProjectFragmentState extends State
17 | with SingleTickerProviderStateMixin, AutomaticKeepAliveClientMixin {
18 | TabController mTabController;
19 | List mTabDatas = [];
20 | int mCurrentIndex = 0;
21 | var mPageController = new PageController(initialPage: 0);
22 | var isPageChanged = true;
23 | bool isNetWorkError = false;
24 |
25 | @override
26 | void initState() {
27 | super.initState();
28 | loadProjectTab();
29 | }
30 |
31 | void loadProjectTab() async {
32 | HttpRequest.getInstance().get(Api.PROJECT_TAB, successCallBack: (data) {
33 | if (data != null) {
34 | List responseJson = json.decode(data);
35 | setState(() {
36 | isNetWorkError = false;
37 | mTabDatas = responseJson.map((m) => ProjectTab.fromJson(m)).toList();
38 | mTabController = TabController(
39 | initialIndex: 0, length: mTabDatas.length, vsync: this);
40 | });
41 | } else {
42 | setState(() {
43 | isNetWorkError = true;
44 | });
45 | }
46 | }, errorCallBack: (code, msg) {});
47 | }
48 |
49 | List initTabs() {
50 | List temps = [];
51 | for (var i = 0; i < mTabDatas.length; i++) {
52 | temps.add(Tab(
53 | text: mTabDatas[i].name,
54 | ));
55 | }
56 | return temps;
57 | }
58 |
59 | initPages() {
60 | List pages = List();
61 | for (ProjectTab item in mTabDatas) {
62 | var page = ProjectListFragment(item.id);
63 | pages.add(page);
64 | }
65 | return pages;
66 | }
67 |
68 | @override
69 | Widget build(BuildContext context) {
70 | var appTheme = Provider.of(context);
71 | return DefaultTabController(
72 | length: mTabDatas.length,
73 | child: isNetWorkError
74 | ? Scaffold(
75 | body: LoadFailWidget(
76 | onTap: () {
77 | loadProjectTab();
78 | },
79 | ),
80 | )
81 | : Scaffold(
82 | appBar: AppBar(
83 | backgroundColor: appTheme.themeColor,
84 | title: TabBar(
85 | controller: mTabController,
86 | tabs: initTabs(),
87 | isScrollable: true,
88 | indicatorColor: Colors.white,
89 | labelColor: Colors.white,
90 | labelStyle: Theme.of(context).textTheme.subtitle,
91 | unselectedLabelColor: Colors.grey,
92 | unselectedLabelStyle: Theme.of(context).textTheme.caption,
93 | ),
94 | ),
95 | body: TabBarView(
96 | controller: mTabController,
97 | children: initPages(),
98 | ),
99 | ),
100 | );
101 | }
102 |
103 | @override
104 | // TODO: implement wantKeepAlive
105 | bool get wantKeepAlive => true;
106 | }
107 |
--------------------------------------------------------------------------------
/lib/page/wenda/wenda_fragment.dart:
--------------------------------------------------------------------------------
1 | import 'dart:convert';
2 |
3 | import 'package:flutter/material.dart';
4 | import 'package:flutter_easyrefresh/easy_refresh.dart';
5 | import 'package:provider/provider.dart';
6 | import 'package:wanandroidflutter/data/article.dart';
7 | import 'package:wanandroidflutter/http/api.dart';
8 | import 'package:wanandroidflutter/http/http_request.dart';
9 | import 'package:wanandroidflutter/theme/theme_model.dart';
10 | import 'package:wanandroidflutter/widget/article_item.dart';
11 | import 'package:wanandroidflutter/widget/custom_refresh.dart';
12 | import 'package:wanandroidflutter/widget/page_widget.dart';
13 |
14 | class WenDaFragment extends StatefulWidget {
15 | @override
16 | _WenDaFragmentState createState() => _WenDaFragmentState();
17 | }
18 |
19 | class _WenDaFragmentState extends State
20 | with AutomaticKeepAliveClientMixin {
21 | List wendaList = List();
22 | int currentPage = 0;
23 | ScrollController _scrollController;
24 | PageStateController _pageStateController;
25 | GlobalKey _easyRefreshKey =
26 | new GlobalKey();
27 |
28 | void _onRefresh(bool up) {
29 | if (up) {
30 | currentPage = 0;
31 | loadWenDaList();
32 | } else {
33 | currentPage++;
34 | loadWenDaList();
35 | }
36 | }
37 |
38 | @override
39 | void initState() {
40 | super.initState();
41 | _pageStateController = PageStateController();
42 | _scrollController = ScrollController();
43 | loadWenDaList();
44 | }
45 |
46 | void loadWenDaList() async {
47 | HttpRequest.getInstance().get("${Api.WENDA_LIST}$currentPage/json",
48 | successCallBack: (data) {
49 | if (currentPage == 0) {
50 | wendaList.clear();
51 | }
52 | _easyRefreshKey.currentState.callRefreshFinish();
53 | _easyRefreshKey.currentState.callLoadMoreFinish();
54 | if (data != null) {
55 | _pageStateController.changeState(PageState.LoadSuccess);
56 | Map dataJson = json.decode(data);
57 | List responseJson = json.decode(json.encode(dataJson["datas"]));
58 | setState(() {
59 | wendaList
60 | .addAll(responseJson.map((m) => Article.fromJson(m)).toList());
61 | });
62 | if (wendaList.length == 0) {
63 | _pageStateController.changeState(PageState.NoData);
64 | }
65 | } else {
66 | _pageStateController.changeState(PageState.LoadFail);
67 | }
68 | }, errorCallBack: (code, msg) {});
69 | }
70 |
71 | @override
72 | Widget build(BuildContext context) {
73 | var appTheme = Provider.of(context);
74 | return Scaffold(
75 | body: PageWidget(
76 | controller: _pageStateController,
77 | reload: () {
78 | loadWenDaList();
79 | },
80 | child: CustomRefresh(
81 | easyRefreshKey: _easyRefreshKey,
82 | onRefresh: () {
83 | _onRefresh(true);
84 | },
85 | loadMore: () {
86 | _onRefresh(false);
87 | },
88 | child: ListView.builder(
89 | controller: _scrollController,
90 | itemCount: wendaList.length,
91 | itemBuilder: (context, index) {
92 | return ArticleWidget(wendaList[index]);
93 | })),
94 | ),
95 | floatingActionButton: FloatingActionButton(
96 | backgroundColor: appTheme.themeColor.withAlpha(180),
97 | child: Icon(Icons.arrow_upward),
98 | onPressed: () {
99 | _scrollController.animateTo(0,
100 | duration: Duration(milliseconds: 1000), curve: Curves.linear);
101 | }),
102 | );
103 | }
104 |
105 | @override
106 | bool get wantKeepAlive => true;
107 | }
108 |
--------------------------------------------------------------------------------
/lib/data/navigation.dart:
--------------------------------------------------------------------------------
1 | class NavigationData {
2 | String name;
3 | List articles;
4 | int cid;
5 |
6 | NavigationData.fromJson(Map json) {
7 | name = json['name'];
8 | if (json['articles'] != null) {
9 | articles = new List();
10 | (json['articles'] as List).forEach((v) {
11 | articles.add(NavigationTag.fromJson(v));
12 | });
13 | }
14 | cid = json['cid'];
15 | }
16 |
17 | Map toJson() {
18 | final Map data = new Map();
19 | data['name'] = this.name;
20 | if (this.articles != null) {
21 | data['articles'] = this.articles.map((v) => v.toJson()).toList();
22 | }
23 | data['cid'] = this.cid;
24 | return data;
25 | }
26 | }
27 |
28 | class NavigationTag {
29 | dynamic shareDate;
30 | String projectLink;
31 | String prefix;
32 | String origin;
33 | String link;
34 | String title;
35 | int type;
36 | int selfVisible;
37 | String apkLink;
38 | String envelopePic;
39 | int audit;
40 | int chapterId;
41 | int id;
42 | int courseId;
43 | String superChapterName;
44 | int publishTime;
45 | String niceShareDate;
46 | int visible;
47 | String niceDate;
48 | String author;
49 | int zan;
50 | String chapterName;
51 | int userId;
52 | List tags;
53 | int superChapterId;
54 | bool fresh;
55 | bool collect;
56 | String shareUser;
57 | String desc;
58 |
59 | NavigationTag.fromJson(Map json) {
60 | shareDate = json['shareDate'];
61 | projectLink = json['projectLink'];
62 | prefix = json['prefix'];
63 | origin = json['origin'];
64 | link = json['link'];
65 | title = json['title'];
66 | type = json['type'];
67 | selfVisible = json['selfVisible'];
68 | apkLink = json['apkLink'];
69 | envelopePic = json['envelopePic'];
70 | audit = json['audit'];
71 | chapterId = json['chapterId'];
72 | id = json['id'];
73 | courseId = json['courseId'];
74 | superChapterName = json['superChapterName'];
75 | publishTime = json['publishTime'];
76 | niceShareDate = json['niceShareDate'];
77 | visible = json['visible'];
78 | niceDate = json['niceDate'];
79 | author = json['author'];
80 | zan = json['zan'];
81 | chapterName = json['chapterName'];
82 | userId = json['userId'];
83 | if (json['tags'] != null) {
84 | tags = new List();
85 | }
86 | superChapterId = json['superChapterId'];
87 | fresh = json['fresh'];
88 | collect = json['collect'];
89 | shareUser = json['shareUser'];
90 | desc = json['desc'];
91 | }
92 |
93 | Map toJson() {
94 | final Map data = new Map();
95 | data['shareDate'] = this.shareDate;
96 | data['projectLink'] = this.projectLink;
97 | data['prefix'] = this.prefix;
98 | data['origin'] = this.origin;
99 | data['link'] = this.link;
100 | data['title'] = this.title;
101 | data['type'] = this.type;
102 | data['selfVisible'] = this.selfVisible;
103 | data['apkLink'] = this.apkLink;
104 | data['envelopePic'] = this.envelopePic;
105 | data['audit'] = this.audit;
106 | data['chapterId'] = this.chapterId;
107 | data['id'] = this.id;
108 | data['courseId'] = this.courseId;
109 | data['superChapterName'] = this.superChapterName;
110 | data['publishTime'] = this.publishTime;
111 | data['niceShareDate'] = this.niceShareDate;
112 | data['visible'] = this.visible;
113 | data['niceDate'] = this.niceDate;
114 | data['author'] = this.author;
115 | data['zan'] = this.zan;
116 | data['chapterName'] = this.chapterName;
117 | data['userId'] = this.userId;
118 | if (this.tags != null) {
119 | data['tags'] = [];
120 | }
121 | data['superChapterId'] = this.superChapterId;
122 | data['fresh'] = this.fresh;
123 | data['collect'] = this.collect;
124 | data['shareUser'] = this.shareUser;
125 | data['desc'] = this.desc;
126 | return data;
127 | }
128 | }
129 |
--------------------------------------------------------------------------------
/lib/page/home/search_result_fragment.dart:
--------------------------------------------------------------------------------
1 | import 'dart:convert';
2 |
3 | import 'package:flutter/material.dart';
4 | import 'package:flutter_easyrefresh/easy_refresh.dart';
5 | import 'package:provider/provider.dart';
6 | import 'package:wanandroidflutter/data/article.dart';
7 | import 'package:wanandroidflutter/http/api.dart';
8 | import 'package:wanandroidflutter/http/http_request.dart';
9 | import 'package:wanandroidflutter/theme/theme_model.dart';
10 | import 'package:wanandroidflutter/widget/article_item.dart';
11 | import 'package:wanandroidflutter/widget/custom_refresh.dart';
12 | import 'package:wanandroidflutter/widget/page_widget.dart';
13 |
14 | class SearchResultFragment extends StatefulWidget {
15 | String keyWord = "";
16 |
17 | SearchResultFragment(this.keyWord);
18 |
19 | @override
20 | _SearchResultFragmentState createState() => _SearchResultFragmentState();
21 | }
22 |
23 | class _SearchResultFragmentState extends State
24 | with AutomaticKeepAliveClientMixin {
25 | int currentPage = 0;
26 | List searchResultList = List();
27 | ScrollController _scrollController;
28 | PageStateController _pageStateController;
29 |
30 | GlobalKey _easyRefreshKey =
31 | new GlobalKey();
32 |
33 | @override
34 | void initState() {
35 | super.initState();
36 | _scrollController = ScrollController();
37 | _pageStateController = PageStateController();
38 | loadSearchList();
39 | }
40 |
41 | void loadSearchList() {
42 | var data = {'k': widget.keyWord};
43 | HttpRequest.getInstance().post("${Api.SEARCH_RESULT_LIST}$currentPage/json",
44 | data: data, successCallBack: (data) {
45 | if (currentPage == 0) {
46 | searchResultList.clear();
47 | }
48 | _easyRefreshKey.currentState.callRefreshFinish();
49 | _easyRefreshKey.currentState.callLoadMoreFinish();
50 | if (data != null) {
51 | _pageStateController.changeState(PageState.LoadSuccess);
52 | Map dataJson = json.decode(data);
53 | List responseJson = json.decode(json.encode(dataJson["datas"]));
54 | setState(() {
55 | searchResultList
56 | .addAll(responseJson.map((m) => Article.fromJson(m)).toList());
57 | });
58 | if (searchResultList.length == 0) {
59 | _pageStateController.changeState(PageState.NoData);
60 | }
61 | } else {
62 | _pageStateController.changeState(PageState.LoadFail);
63 | }
64 | }, errorCallBack: (code, msg) {});
65 | }
66 |
67 | void _onRefresh(bool up) {
68 | if (up) {
69 | currentPage = 0;
70 | loadSearchList();
71 | } else {
72 | currentPage++;
73 | loadSearchList();
74 | }
75 | }
76 |
77 | @override
78 | Widget build(BuildContext context) {
79 | var appTheme = Provider.of(context);
80 | return Scaffold(
81 | body: PageWidget(
82 | controller: _pageStateController,
83 | reload: () {
84 | loadSearchList();
85 | },
86 | child: CustomRefresh(
87 | easyRefreshKey: _easyRefreshKey,
88 | onRefresh: () {
89 | _onRefresh(true);
90 | },
91 | loadMore: () {
92 | _onRefresh(false);
93 | },
94 | child: ListView.builder(
95 | controller: _scrollController,
96 | itemCount: searchResultList.length,
97 | itemBuilder: (context, index) {
98 | return ArticleWidget(searchResultList[index]);
99 | })),
100 | ),
101 | floatingActionButton: FloatingActionButton(
102 | backgroundColor: appTheme.themeColor.withAlpha(180),
103 | child: Icon(Icons.arrow_upward),
104 | onPressed: () {
105 | _scrollController.animateTo(0,
106 | duration: Duration(milliseconds: 1000), curve: Curves.linear);
107 | }),
108 | );
109 | }
110 |
111 | @override
112 | // TODO: implement wantKeepAlive
113 | bool get wantKeepAlive => true;
114 | }
115 |
--------------------------------------------------------------------------------
/lib/page/system/knowledge/KnowledgeListFragment.dart:
--------------------------------------------------------------------------------
1 | import 'dart:convert';
2 |
3 | import 'package:flutter/material.dart';
4 | import 'package:flutter_easyrefresh/easy_refresh.dart';
5 | import 'package:provider/provider.dart';
6 | import 'package:wanandroidflutter/data/article.dart';
7 | import 'package:wanandroidflutter/http/api.dart';
8 | import 'package:wanandroidflutter/http/http_request.dart';
9 | import 'package:wanandroidflutter/theme/theme_model.dart';
10 | import 'package:wanandroidflutter/widget/article_item.dart';
11 | import 'package:wanandroidflutter/widget/custom_refresh.dart';
12 | import 'package:wanandroidflutter/widget/page_widget.dart';
13 |
14 | class KnowledgeListFragment extends StatefulWidget {
15 | int _Id;
16 |
17 | KnowledgeListFragment(this._Id);
18 |
19 | @override
20 | _KnowledgeListFragmentState createState() => _KnowledgeListFragmentState(_Id);
21 | }
22 |
23 | class _KnowledgeListFragmentState extends State
24 | with AutomaticKeepAliveClientMixin {
25 | int _Id;
26 | int currentPage = 0;
27 | List knowledgeList = List();
28 | ScrollController _scrollController;
29 | PageStateController _pageStateController;
30 |
31 | _KnowledgeListFragmentState(this._Id);
32 |
33 | GlobalKey _easyRefreshKey =
34 | new GlobalKey();
35 |
36 | void _onRefresh(bool up) {
37 | if (up) {
38 | currentPage = 0;
39 | loadKnowledgeData();
40 | } else {
41 | currentPage++;
42 | loadKnowledgeData();
43 | }
44 | }
45 |
46 | @override
47 | void initState() {
48 | // TODO: implement initState
49 | super.initState();
50 | _pageStateController = PageStateController();
51 | _scrollController = ScrollController();
52 | loadKnowledgeData();
53 | }
54 |
55 | void loadKnowledgeData() async {
56 | var params = {"cid": _Id};
57 | HttpRequest.getInstance().get("${Api.HOME_ARTICLE_LIST}$currentPage/json",
58 | data: params, successCallBack: (data) {
59 | if (currentPage == 0) {
60 | knowledgeList.clear();
61 | }
62 | _easyRefreshKey.currentState.callRefreshFinish();
63 | _easyRefreshKey.currentState.callLoadMoreFinish();
64 | if (data != null) {
65 | _pageStateController.changeState(PageState.LoadSuccess);
66 | Map dataJson = json.decode(data);
67 | List responseJson = json.decode(json.encode(dataJson["datas"]));
68 | setState(() {
69 | knowledgeList
70 | .addAll(responseJson.map((m) => Article.fromJson(m)).toList());
71 | });
72 | if (knowledgeList.length == 0) {
73 | _pageStateController.changeState(PageState.NoData);
74 | }
75 | } else {
76 | _pageStateController.changeState(PageState.LoadFail);
77 | }
78 | }, errorCallBack: (code, msg) {});
79 | }
80 |
81 | @override
82 | Widget build(BuildContext context) {
83 | var appTheme = Provider.of(context);
84 | return Scaffold(
85 | body: PageWidget(
86 | controller: _pageStateController,
87 | reload: () {
88 | loadKnowledgeData();
89 | },
90 | child: CustomRefresh(
91 | easyRefreshKey: _easyRefreshKey,
92 | onRefresh: () {
93 | _onRefresh(true);
94 | },
95 | loadMore: () {
96 | _onRefresh(false);
97 | },
98 | child: ListView.builder(
99 | controller: _scrollController,
100 | itemCount: knowledgeList.length,
101 | itemBuilder: (context, index) {
102 | return ArticleWidget(knowledgeList[index]);
103 | })),
104 | ),
105 | floatingActionButton: FloatingActionButton(
106 | backgroundColor: appTheme.themeColor.withAlpha(180),
107 | child: Icon(Icons.arrow_upward),
108 | onPressed: () {
109 | _scrollController.animateTo(0,
110 | duration: Duration(milliseconds: 1000), curve: Curves.linear);
111 | }),
112 | );
113 | }
114 |
115 | @override
116 | // TODO: implement wantKeepAlive
117 | bool get wantKeepAlive => true;
118 | }
119 |
--------------------------------------------------------------------------------
/lib/page/rank/rank_fragment.dart:
--------------------------------------------------------------------------------
1 | import 'dart:convert';
2 |
3 | import 'package:flutter/material.dart';
4 | import 'package:flutter_easyrefresh/easy_refresh.dart';
5 | import 'package:provider/provider.dart';
6 | import 'package:wanandroidflutter/data/rank.dart';
7 | import 'package:wanandroidflutter/http/api.dart';
8 | import 'package:wanandroidflutter/http/http_request.dart';
9 | import 'package:wanandroidflutter/theme/dark_model.dart';
10 | import 'package:wanandroidflutter/theme/theme_model.dart';
11 | import 'package:wanandroidflutter/widget/coin_item.dart';
12 | import 'package:wanandroidflutter/widget/custom_refresh.dart';
13 | import 'package:wanandroidflutter/widget/page_widget.dart';
14 |
15 | class RankFragment extends StatefulWidget {
16 | @override
17 | _RankFragmentState createState() => _RankFragmentState();
18 | }
19 |
20 | class _RankFragmentState extends State
21 | with AutomaticKeepAliveClientMixin {
22 | List rankList = List();
23 | int currentPage = 1;
24 | ScrollController _scrollController;
25 | PageStateController _pageStateController;
26 | GlobalKey _easyRefreshKey =
27 | new GlobalKey();
28 | RankData coinData;
29 |
30 | void getCoinCount() async {
31 | HttpRequest.getInstance().get(Api.COIN_INFO, successCallBack: (data) {
32 | if (data != null && data.isNotEmpty) {
33 | Map coinMap = json.decode(data);
34 | setState(() {
35 | coinData = RankData.fromJson(coinMap);
36 | });
37 | }
38 | }, errorCallBack: (code, msg) {});
39 | }
40 |
41 | @override
42 | void initState() {
43 | super.initState();
44 | _pageStateController = PageStateController();
45 | _scrollController = ScrollController();
46 | loadRankList();
47 | getCoinCount();
48 | }
49 |
50 | void _onRefresh(bool up) {
51 | if (up) {
52 | currentPage = 1;
53 | loadRankList();
54 | } else {
55 | currentPage++;
56 | loadRankList();
57 | }
58 | }
59 |
60 | void loadRankList() async {
61 | HttpRequest.getInstance().get("${Api.RANK_LIST}$currentPage/json",
62 | successCallBack: (data) {
63 | if (currentPage == 1) {
64 | rankList.clear();
65 | }
66 | _easyRefreshKey.currentState.callRefreshFinish();
67 | _easyRefreshKey.currentState.callLoadMoreFinish();
68 | if (data != null) {
69 | _pageStateController.changeState(PageState.LoadSuccess);
70 | Map dataJson = json.decode(data);
71 | List responseJson = json.decode(json.encode(dataJson["datas"]));
72 | setState(() {
73 | rankList
74 | .addAll(responseJson.map((m) => RankData.fromJson(m)).toList());
75 | });
76 | if (rankList.length == 0) {
77 | _pageStateController.changeState(PageState.NoData);
78 | }
79 | } else {
80 | _pageStateController.changeState(PageState.LoadFail);
81 | }
82 | }, errorCallBack: (code, msg) {});
83 | }
84 |
85 | @override
86 | Widget build(BuildContext context) {
87 | var isDarkMode = Provider.of(context).isDark;
88 | return Scaffold(
89 | body: Stack(children: [
90 | PageWidget(
91 | controller: _pageStateController,
92 | reload: () {
93 | loadRankList();
94 | },
95 | child: CustomRefresh(
96 | easyRefreshKey: _easyRefreshKey,
97 | onRefresh: () {
98 | _onRefresh(true);
99 | },
100 | loadMore: () {
101 | _onRefresh(false);
102 | },
103 | child: ListView.builder(
104 | controller: _scrollController,
105 | itemCount: rankList.length,
106 | itemBuilder: (context, index) {
107 | return CoinRankWidget(rankList[index]);
108 | })),
109 | ),
110 | Align(
111 | alignment: Alignment.bottomCenter,
112 | child: Container(
113 | child: CoinRankWidget(coinData),
114 | color: isDarkMode ? Color(0xff323638) : Color(0xFFCDCDCD),
115 | ),
116 | )
117 | ]));
118 | }
119 |
120 | @override
121 | bool get wantKeepAlive => true;
122 | }
123 |
--------------------------------------------------------------------------------
/lib/page/share/share_article_fragment.dart:
--------------------------------------------------------------------------------
1 | import 'dart:convert';
2 |
3 | import 'package:flutter/material.dart';
4 | import 'package:flutter_easyrefresh/easy_refresh.dart';
5 | import 'package:provider/provider.dart';
6 | import 'package:wanandroidflutter/application.dart';
7 | import 'package:wanandroidflutter/data/article.dart';
8 | import 'package:wanandroidflutter/http/api.dart';
9 | import 'package:wanandroidflutter/http/http_request.dart';
10 | import 'package:wanandroidflutter/theme/theme_model.dart';
11 | import 'package:wanandroidflutter/utils/refresh_event.dart';
12 | import 'package:wanandroidflutter/widget/custom_refresh.dart';
13 | import 'package:wanandroidflutter/widget/page_widget.dart';
14 | import 'package:wanandroidflutter/widget/share_item.dart';
15 |
16 | class ShareArticleFragment extends StatefulWidget {
17 | @override
18 | _ShareArticleFragmentState createState() => _ShareArticleFragmentState();
19 | }
20 |
21 | class _ShareArticleFragmentState extends State
22 | with AutomaticKeepAliveClientMixin {
23 | int currentPage = 1;
24 | List shareArticleList = List();
25 | ScrollController _scrollController;
26 | PageStateController _pageStateController;
27 | GlobalKey _easyRefreshKey =
28 | new GlobalKey();
29 |
30 | void _onRefresh(bool up) {
31 | if (up) {
32 | currentPage = 1;
33 | loadShareArticleList();
34 | } else {
35 | currentPage++;
36 | loadShareArticleList();
37 | }
38 | }
39 |
40 | @override
41 | void initState() {
42 | // TODO: implement initState
43 | super.initState();
44 | _pageStateController = PageStateController();
45 | _scrollController = ScrollController();
46 | Application.eventBus.on().listen((event) {
47 | setState(() {
48 | _onRefresh(true);
49 | });
50 | });
51 | loadShareArticleList();
52 | }
53 |
54 | void loadShareArticleList() async {
55 | HttpRequest.getInstance().get("${Api.SHARE_ARTICLE_LIST}$currentPage/json",
56 | successCallBack: (data) {
57 | if (currentPage == 1) {
58 | shareArticleList.clear();
59 | }
60 | _easyRefreshKey.currentState.callRefreshFinish();
61 | _easyRefreshKey.currentState.callLoadMoreFinish();
62 | if (data != null) {
63 | _pageStateController.changeState(PageState.LoadSuccess);
64 | Map dataJson = json.decode(data);
65 | List responseJson =
66 | json.decode(json.encode(dataJson["shareArticles"]["datas"]));
67 | print(responseJson);
68 | setState(() {
69 | shareArticleList
70 | .addAll(responseJson.map((m) => Article.fromJson(m)).toList());
71 | });
72 | if (shareArticleList.length == 0) {
73 | _pageStateController.changeState(PageState.NoData);
74 | }
75 | } else {
76 | _pageStateController.changeState(PageState.LoadFail);
77 | }
78 | }, errorCallBack: (code, msg) {});
79 | }
80 |
81 | @override
82 | Widget build(BuildContext context) {
83 | var appTheme = Provider.of(context);
84 | return Scaffold(
85 | body: PageWidget(
86 | controller: _pageStateController,
87 | reload: () {
88 | loadShareArticleList();
89 | },
90 | child: CustomRefresh(
91 | easyRefreshKey: _easyRefreshKey,
92 | onRefresh: () {
93 | _onRefresh(true);
94 | },
95 | loadMore: () {
96 | _onRefresh(false);
97 | },
98 | child: ListView.builder(
99 | controller: _scrollController,
100 | itemCount: shareArticleList.length,
101 | itemBuilder: (context, index) {
102 | return ShareWidget(shareArticleList[index]);
103 | })),
104 | ),
105 | floatingActionButton: FloatingActionButton(
106 | backgroundColor: appTheme.themeColor.withAlpha(180),
107 | child: Icon(Icons.arrow_upward),
108 | onPressed: () {
109 | _scrollController.animateTo(0,
110 | duration: Duration(milliseconds: 1000), curve: Curves.linear);
111 | }),
112 | );
113 | }
114 |
115 | @override
116 | // TODO: implement wantKeepAlive
117 | bool get wantKeepAlive => true;
118 | }
119 |
--------------------------------------------------------------------------------
/lib/page/system/navigation/NavigationFragment.dart:
--------------------------------------------------------------------------------
1 | import 'dart:convert';
2 |
3 | import 'package:flutter/material.dart';
4 | import 'package:provider/provider.dart';
5 | import 'package:wanandroidflutter/data/navigation.dart';
6 | import 'package:wanandroidflutter/http/api.dart';
7 | import 'package:wanandroidflutter/http/http_request.dart';
8 | import 'package:wanandroidflutter/page/webview_page.dart';
9 | import 'package:wanandroidflutter/theme/theme_model.dart';
10 | import 'package:wanandroidflutter/utils/common.dart';
11 | import 'package:wanandroidflutter/widget/load_fail_widget.dart';
12 |
13 | class NavigationFragment extends StatefulWidget {
14 | @override
15 | _NavigationFragmentState createState() => _NavigationFragmentState();
16 | }
17 |
18 | class _NavigationFragmentState extends State {
19 | List navigationList = [];
20 | var appTheme;
21 | bool isNetWorkError = false;
22 |
23 | @override
24 | void initState() {
25 | super.initState();
26 | loadNavigationList();
27 | }
28 |
29 | void loadNavigationList() async {
30 | HttpRequest.getInstance().get(Api.NAVIGATION, successCallBack: (data) {
31 | if (data != null) {
32 | List responseJson = json.decode(data);
33 | setState(() {
34 | isNetWorkError = false;
35 | navigationList.addAll(
36 | responseJson.map((m) => NavigationData.fromJson(m)).toList());
37 | });
38 | } else {
39 | setState(() {
40 | isNetWorkError = true;
41 | });
42 | }
43 | }, errorCallBack: (code, msg) {});
44 | }
45 |
46 | @override
47 | Widget build(BuildContext context) {
48 | appTheme = Provider.of(context);
49 | return Container(
50 | decoration: BoxDecoration(
51 | color: Theme.of(context).iconTheme.color.withAlpha(0),
52 | ),
53 | child: isNetWorkError
54 | ? Scaffold(
55 | body: LoadFailWidget(
56 | onTap: () {
57 | loadNavigationList();
58 | },
59 | ),
60 | )
61 | : Scrollbar(
62 | child: ListView.builder(
63 | padding: EdgeInsets.all(15),
64 | itemCount: navigationList.length,
65 | itemBuilder: (context, index) {
66 | return NavigationCategoryWidget(
67 | navigationData: navigationList[index],
68 | );
69 | }),
70 | ),
71 | );
72 | }
73 | }
74 |
75 | class NavigationCategoryWidget extends StatelessWidget {
76 | final NavigationData navigationData;
77 |
78 | NavigationCategoryWidget({Key key, this.navigationData});
79 |
80 | @override
81 | Widget build(BuildContext context) {
82 | var appTheme = Provider.of(context);
83 | return Padding(
84 | padding: EdgeInsets.symmetric(vertical: 10),
85 | child: Column(
86 | crossAxisAlignment: CrossAxisAlignment.start,
87 | children: [
88 | Text(
89 | navigationData.name,
90 | style: Theme.of(context).textTheme.subtitle,
91 | ),
92 | Wrap(
93 | spacing: 10,
94 | children: List.generate(
95 | navigationData.articles.length,
96 | (index) => ActionChip(
97 | onPressed: () {
98 | CommonUtils.push(
99 | context,
100 | WebViewPage(
101 | url: navigationData.articles[index].link,
102 | title: navigationData.articles[index].title,
103 | id: navigationData.articles[index].id,
104 | isCollect: navigationData.articles[index].collect,
105 | ));
106 | },
107 | backgroundColor:
108 | Theme.of(context).iconTheme.color.withAlpha(20),
109 | label: Text(
110 | navigationData.articles[index].title,
111 | maxLines: 1,
112 | style: TextStyle(
113 | fontSize: 13, color: CommonUtils.getRandomColor()),
114 | ),
115 | )),
116 | )
117 | ],
118 | ),
119 | );
120 | }
121 | }
122 |
--------------------------------------------------------------------------------
/lib/page/system/knowledge/KnowledgeFragment.dart:
--------------------------------------------------------------------------------
1 | import 'dart:convert';
2 |
3 | import 'package:flutter/material.dart';
4 | import 'package:provider/provider.dart';
5 | import 'package:wanandroidflutter/data/knowledge.dart';
6 | import 'package:wanandroidflutter/http/api.dart';
7 | import 'package:wanandroidflutter/http/http_request.dart';
8 | import 'package:wanandroidflutter/page/system/knowledge/KnowledgeListFragment.dart';
9 | import 'package:wanandroidflutter/theme/theme_model.dart';
10 | import 'package:wanandroidflutter/utils/common.dart';
11 | import 'package:wanandroidflutter/widget/load_fail_widget.dart';
12 |
13 | class KnowledgeFragment extends StatefulWidget {
14 | @override
15 | _KnowledgeFragmentState createState() => _KnowledgeFragmentState();
16 | }
17 |
18 | class _KnowledgeFragmentState extends State {
19 | List knowLedgeList = [];
20 | var appTheme;
21 | bool isNetWorkError = false;
22 |
23 | @override
24 | void initState() {
25 | super.initState();
26 | loadKnowledgeList();
27 | }
28 |
29 | void loadKnowledgeList() async {
30 | HttpRequest.getInstance().get(Api.TREE, successCallBack: (data) {
31 | if (data != null) {
32 | List responseJson = json.decode(data);
33 | setState(() {
34 | isNetWorkError = false;
35 | knowLedgeList.addAll(
36 | responseJson.map((m) => KnowledgeData.fromJson(m)).toList());
37 | });
38 | } else {
39 | setState(() {
40 | isNetWorkError = true;
41 | });
42 | }
43 | }, errorCallBack: (code, msg) {});
44 | }
45 |
46 | @override
47 | Widget build(BuildContext context) {
48 | appTheme = Provider.of(context);
49 | return Container(
50 | decoration: BoxDecoration(
51 | color: Theme.of(context).iconTheme.color.withAlpha(0),
52 | ),
53 | child: isNetWorkError
54 | ? Scaffold(
55 | body: LoadFailWidget(
56 | onTap: () {
57 | loadKnowledgeList();
58 | },
59 | ),
60 | )
61 | : Scrollbar(
62 | child: ListView.builder(
63 | padding: EdgeInsets.all(15),
64 | itemCount: knowLedgeList.length,
65 | itemBuilder: (context, index) {
66 | return KnowledgeCategoryWidget(
67 | knowledgeData: knowLedgeList[index],
68 | );
69 | }),
70 | ),
71 | );
72 | }
73 | }
74 |
75 | class KnowledgeCategoryWidget extends StatelessWidget {
76 | final KnowledgeData knowledgeData;
77 |
78 | KnowledgeCategoryWidget({Key key, this.knowledgeData});
79 |
80 | @override
81 | Widget build(BuildContext context) {
82 | var appTheme = Provider.of(context);
83 | return Padding(
84 | padding: EdgeInsets.symmetric(vertical: 10),
85 | child: Column(
86 | crossAxisAlignment: CrossAxisAlignment.start,
87 | children: [
88 | Text(
89 | knowledgeData.name,
90 | style: Theme.of(context).textTheme.subtitle,
91 | ),
92 | Wrap(
93 | spacing: 10,
94 | children: List.generate(
95 | knowledgeData.children.length,
96 | (index) => ActionChip(
97 | onPressed: () {
98 | CommonUtils.push(
99 | context,
100 | Scaffold(
101 | appBar: AppBar(
102 | title: Text(knowledgeData.children[index].name
103 | .toString()),
104 | backgroundColor: appTheme.themeColor,
105 | centerTitle: true,
106 | ),
107 | body: KnowledgeListFragment(
108 | knowledgeData.children[index].id),
109 | ));
110 | },
111 | backgroundColor:
112 | Theme.of(context).iconTheme.color.withAlpha(20),
113 | label: Text(
114 | knowledgeData.children[index].name,
115 | maxLines: 1,
116 | style: TextStyle(
117 | fontSize: 13, color: CommonUtils.getRandomColor()),
118 | ),
119 | )),
120 | )
121 | ],
122 | ),
123 | );
124 | }
125 | }
126 |
--------------------------------------------------------------------------------
/lib/page/square/square_fragment.dart:
--------------------------------------------------------------------------------
1 | import 'dart:convert';
2 |
3 | import 'package:flutter/material.dart';
4 | import 'package:flutter_easyrefresh/easy_refresh.dart';
5 | import 'package:provider/provider.dart';
6 | import 'package:wanandroidflutter/data/article.dart';
7 | import 'package:wanandroidflutter/http/api.dart';
8 | import 'package:wanandroidflutter/http/http_request.dart';
9 | import 'package:wanandroidflutter/page/input/share_fragment.dart';
10 | import 'package:wanandroidflutter/theme/theme_model.dart';
11 | import 'package:wanandroidflutter/utils/common.dart';
12 | import 'package:wanandroidflutter/widget/article_item.dart';
13 | import 'package:wanandroidflutter/widget/custom_refresh.dart';
14 | import 'package:wanandroidflutter/widget/page_widget.dart';
15 |
16 | class SquareFragment extends StatefulWidget {
17 | @override
18 | _SquareFragmentState createState() => _SquareFragmentState();
19 | }
20 |
21 | class _SquareFragmentState extends State
22 | with AutomaticKeepAliveClientMixin {
23 | List squareList = List();
24 | int currentPage = 0;
25 | ScrollController _scrollController;
26 | PageStateController _pageStateController;
27 | GlobalKey _easyRefreshKey =
28 | new GlobalKey();
29 |
30 | void _onRefresh(bool up) {
31 | if (up) {
32 | currentPage = 0;
33 | loadSquareList();
34 | } else {
35 | currentPage++;
36 | loadSquareList();
37 | }
38 | }
39 |
40 | @override
41 | void initState() {
42 | super.initState();
43 | _pageStateController = PageStateController();
44 | _scrollController = ScrollController();
45 | loadSquareList();
46 | }
47 |
48 | void loadSquareList() async {
49 | HttpRequest.getInstance().get("${Api.SQUARE_LIST}$currentPage/json",
50 | successCallBack: (data) {
51 | if (currentPage == 0) {
52 | squareList.clear();
53 | }
54 | _easyRefreshKey.currentState.callRefreshFinish();
55 | _easyRefreshKey.currentState.callLoadMoreFinish();
56 | if (data != null) {
57 | _pageStateController.changeState(PageState.LoadSuccess);
58 | Map dataJson = json.decode(data);
59 | List responseJson = json.decode(json.encode(dataJson["datas"]));
60 | setState(() {
61 | squareList
62 | .addAll(responseJson.map((m) => Article.fromJson(m)).toList());
63 | });
64 | if (squareList.length == 0) {
65 | _pageStateController.changeState(PageState.NoData);
66 | }
67 | } else {
68 | _pageStateController.changeState(PageState.LoadFail);
69 | }
70 | }, errorCallBack: (code, msg) {});
71 | }
72 |
73 | @override
74 | Widget build(BuildContext context) {
75 | var appTheme = Provider.of(context);
76 | return Scaffold(
77 | body: PageWidget(
78 | controller: _pageStateController,
79 | reload: () {
80 | loadSquareList();
81 | },
82 | child: CustomRefresh(
83 | easyRefreshKey: _easyRefreshKey,
84 | onRefresh: () {
85 | _onRefresh(true);
86 | },
87 | loadMore: () {
88 | _onRefresh(false);
89 | },
90 | child: ListView.builder(
91 | controller: _scrollController,
92 | itemCount: squareList.length,
93 | itemBuilder: (context, index) {
94 | return ArticleWidget(squareList[index]);
95 | })),
96 | ),
97 | floatingActionButton: FloatingActionButton(
98 | backgroundColor: appTheme.themeColor.withAlpha(180),
99 | child: Icon(Icons.add),
100 | onPressed: () {
101 | _inAddShare();
102 | }),
103 | );
104 | }
105 |
106 | //分享文章
107 | void _inAddShare() {
108 | showDialog(
109 | context: context,
110 | barrierDismissible: false, // user must tap button!
111 | builder: (BuildContext context) {
112 | return SimpleInputDialogLayout(
113 | isCollectArticle: false,
114 | isDIYText: true,
115 | themeText: "分享",
116 | dialogTitleText: "分享文章",
117 | confirmCallback2: (collectTitle, collectUrl) async {
118 | //收藏文章
119 | var data;
120 | data = {'title': collectTitle, 'link': collectUrl};
121 | HttpRequest.getInstance().post(Api.SHARE_ARTICLE, data: data,
122 | successCallBack: (data) {
123 | CommonUtils.toast("分享文章成功");
124 | _onRefresh(true);
125 | });
126 | },
127 | );
128 | });
129 | }
130 |
131 | @override
132 | bool get wantKeepAlive => true;
133 | }
134 |
--------------------------------------------------------------------------------
/lib/generated/intl/messages_zh.dart:
--------------------------------------------------------------------------------
1 | // DO NOT EDIT. This is code generated via package:intl/generate_localized.dart
2 | // This is a library that provides messages for a zh locale. All the
3 | // messages from the main program should be duplicated here with the same
4 | // function name.
5 |
6 | // Ignore issues from commonly used lints in this file.
7 | // ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new
8 | // ignore_for_file:prefer_single_quotes,comment_references, directives_ordering
9 | // ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases
10 | // ignore_for_file:unused_import, file_names
11 |
12 | import 'package:intl/intl.dart';
13 | import 'package:intl/message_lookup_by_library.dart';
14 |
15 | final messages = new MessageLookup();
16 |
17 | typedef String MessageIfAbsent(String messageStr, List args);
18 |
19 | class MessageLookup extends MessageLookupByLibrary {
20 | String get localeName => 'zh';
21 |
22 | final messages = _notInlinedMessages(_notInlinedMessages);
23 | static _notInlinedMessages(_) => {
24 | "about" : MessageLookupByLibrary.simpleMessage("关于"),
25 | "autoBySystem" : MessageLookupByLibrary.simpleMessage("跟随系统"),
26 | "cancel" : MessageLookupByLibrary.simpleMessage("取消"),
27 | "clear_cache" : MessageLookupByLibrary.simpleMessage("清空缓存"),
28 | "clear_cache_tip" : MessageLookupByLibrary.simpleMessage("确定清除缓存吗?"),
29 | "clear_history" : MessageLookupByLibrary.simpleMessage("清空"),
30 | "collect" : MessageLookupByLibrary.simpleMessage("收藏"),
31 | "complete_refresh" : MessageLookupByLibrary.simpleMessage("刷新完成"),
32 | "confirm" : MessageLookupByLibrary.simpleMessage("确认"),
33 | "copy_link" : MessageLookupByLibrary.simpleMessage("复制链接"),
34 | "copy_tip" : MessageLookupByLibrary.simpleMessage("复制成功"),
35 | "crop_image" : MessageLookupByLibrary.simpleMessage("裁剪头像"),
36 | "down_refresh" : MessageLookupByLibrary.simpleMessage("下拉刷新"),
37 | "exit" : MessageLookupByLibrary.simpleMessage("退出"),
38 | "hot_search" : MessageLookupByLibrary.simpleMessage("热门搜索"),
39 | "input_search" : MessageLookupByLibrary.simpleMessage("请输入搜索关键字"),
40 | "integral" : MessageLookupByLibrary.simpleMessage("积分: "),
41 | "integral_rank" : MessageLookupByLibrary.simpleMessage("积分排行"),
42 | "kuaile_font" : MessageLookupByLibrary.simpleMessage("喵趣字体"),
43 | "language_setting" : MessageLookupByLibrary.simpleMessage("语言设置"),
44 | "level" : MessageLookupByLibrary.simpleMessage("排名: "),
45 | "login" : MessageLookupByLibrary.simpleMessage("登录"),
46 | "login_tip" : MessageLookupByLibrary.simpleMessage("点击头像登录"),
47 | "loginout" : MessageLookupByLibrary.simpleMessage("退出登录"),
48 | "loginoutTip" : MessageLookupByLibrary.simpleMessage("登出成功"),
49 | "me_collect" : MessageLookupByLibrary.simpleMessage("我的收藏"),
50 | "me_share" : MessageLookupByLibrary.simpleMessage("我的分享"),
51 | "my_blog" : MessageLookupByLibrary.simpleMessage("我的博客"),
52 | "network_error" : MessageLookupByLibrary.simpleMessage("网络异常,点击重试!"),
53 | "new_article" : MessageLookupByLibrary.simpleMessage("新"),
54 | "night_mode" : MessageLookupByLibrary.simpleMessage("夜间模式"),
55 | "no_data" : MessageLookupByLibrary.simpleMessage("没有数据,请重试!"),
56 | "normol_font" : MessageLookupByLibrary.simpleMessage("正常字体"),
57 | "password" : MessageLookupByLibrary.simpleMessage("请输入密码"),
58 | "rank" : MessageLookupByLibrary.simpleMessage("积分排行榜"),
59 | "refreshing" : MessageLookupByLibrary.simpleMessage("刷新中"),
60 | "register" : MessageLookupByLibrary.simpleMessage("注册"),
61 | "release_refresh" : MessageLookupByLibrary.simpleMessage("释放刷新"),
62 | "repassword" : MessageLookupByLibrary.simpleMessage("请输入密码"),
63 | "search" : MessageLookupByLibrary.simpleMessage("搜索"),
64 | "search_history" : MessageLookupByLibrary.simpleMessage("搜索记录"),
65 | "search_tip" : MessageLookupByLibrary.simpleMessage("输入字段为空"),
66 | "setting" : MessageLookupByLibrary.simpleMessage("设置"),
67 | "share_to_square" : MessageLookupByLibrary.simpleMessage("分享到广场"),
68 | "square" : MessageLookupByLibrary.simpleMessage("广场"),
69 | "switching_fonts" : MessageLookupByLibrary.simpleMessage("切换字体"),
70 | "tab_home" : MessageLookupByLibrary.simpleMessage("首页"),
71 | "tab_navigation" : MessageLookupByLibrary.simpleMessage("导航"),
72 | "tab_project" : MessageLookupByLibrary.simpleMessage("项目"),
73 | "tab_system" : MessageLookupByLibrary.simpleMessage("体系"),
74 | "tab_wechat" : MessageLookupByLibrary.simpleMessage("公众号"),
75 | "theme" : MessageLookupByLibrary.simpleMessage("主题"),
76 | "theme_choose" : MessageLookupByLibrary.simpleMessage("主题颜色选择"),
77 | "theme_tips" : MessageLookupByLibrary.simpleMessage("夜间模式下不可以更改主题嗷~"),
78 | "top" : MessageLookupByLibrary.simpleMessage("置顶"),
79 | "up_refresh" : MessageLookupByLibrary.simpleMessage("上拉刷新"),
80 | "update_time" : MessageLookupByLibrary.simpleMessage("更新时间: "),
81 | "username" : MessageLookupByLibrary.simpleMessage("请输入用户名"),
82 | "wenda" : MessageLookupByLibrary.simpleMessage("问答")
83 | };
84 | }
85 |
--------------------------------------------------------------------------------
/lib/data/article.dart:
--------------------------------------------------------------------------------
1 | class Article {
2 | int shareDate;
3 | String projectLink;
4 | String prefix;
5 | String origin;
6 | int originId;
7 | String link;
8 | String title;
9 | int type;
10 | int selfVisible;
11 | String apkLink;
12 | String envelopePic;
13 | int audit;
14 | int chapterId;
15 | int id;
16 | int courseId;
17 | String superChapterName;
18 | int publishTime;
19 | String niceShareDate;
20 | int visible;
21 | String niceDate;
22 | String author;
23 | int zan;
24 | String chapterName;
25 | int userId;
26 | List tags;
27 | int superChapterId;
28 | bool fresh;
29 | bool collect;
30 | String shareUser;
31 | String desc;
32 |
33 | Article(
34 | {this.shareDate,
35 | this.projectLink,
36 | this.prefix,
37 | this.origin,
38 | this.originId,
39 | this.link,
40 | this.title,
41 | this.type,
42 | this.selfVisible,
43 | this.apkLink,
44 | this.envelopePic,
45 | this.audit,
46 | this.chapterId,
47 | this.id,
48 | this.courseId,
49 | this.superChapterName,
50 | this.publishTime,
51 | this.niceShareDate,
52 | this.visible,
53 | this.niceDate,
54 | this.author,
55 | this.zan,
56 | this.chapterName,
57 | this.userId,
58 | this.tags,
59 | this.superChapterId,
60 | this.fresh,
61 | this.collect,
62 | this.shareUser,
63 | this.desc});
64 |
65 | Article.fromJson(Map json) {
66 | shareDate = json['shareDate'];
67 | projectLink = json['projectLink'];
68 | prefix = json['prefix'];
69 | originId = json['originId'];
70 | origin = json['origin'];
71 | link = json['link'];
72 | title = json['title'];
73 | type = json['type'];
74 | selfVisible = json['selfVisible'];
75 | apkLink = json['apkLink'];
76 | envelopePic = json['envelopePic'];
77 | audit = json['audit'];
78 | chapterId = json['chapterId'];
79 | id = json['id'];
80 | courseId = json['courseId'];
81 | superChapterName = json['superChapterName'];
82 | publishTime = json['publishTime'];
83 | niceShareDate = json['niceShareDate'];
84 | visible = json['visible'];
85 | niceDate = json['niceDate'];
86 | author = json['author'];
87 | zan = json['zan'];
88 | chapterName = json['chapterName'];
89 | userId = json['userId'];
90 | if (json['tags'] != null) {
91 | tags = new List();
92 | (json['tags'] as List).forEach((v) {
93 | tags.add(new ArticleTag.fromJson(v));
94 | });
95 | }
96 | superChapterId = json['superChapterId'];
97 | fresh = json['fresh'];
98 | collect = json['collect'];
99 | shareUser = json['shareUser'];
100 | desc = json['desc'];
101 | }
102 |
103 | Map toJson() {
104 | final Map data = new Map();
105 | data['shareDate'] = this.shareDate;
106 | data['projectLink'] = this.projectLink;
107 | data['prefix'] = this.prefix;
108 | data['origin'] = this.origin;
109 | data['originId'] = this.originId;
110 | data['link'] = this.link;
111 | data['title'] = this.title;
112 | data['type'] = this.type;
113 | data['selfVisible'] = this.selfVisible;
114 | data['apkLink'] = this.apkLink;
115 | data['envelopePic'] = this.envelopePic;
116 | data['audit'] = this.audit;
117 | data['chapterId'] = this.chapterId;
118 | data['id'] = this.id;
119 | data['courseId'] = this.courseId;
120 | data['superChapterName'] = this.superChapterName;
121 | data['publishTime'] = this.publishTime;
122 | data['niceShareDate'] = this.niceShareDate;
123 | data['visible'] = this.visible;
124 | data['niceDate'] = this.niceDate;
125 | data['author'] = this.author;
126 | data['zan'] = this.zan;
127 | data['chapterName'] = this.chapterName;
128 | data['userId'] = this.userId;
129 | if (this.tags != null) {
130 | data['tags'] = this.tags.map((v) => v.toJson()).toList();
131 | }
132 | data['superChapterId'] = this.superChapterId;
133 | data['fresh'] = this.fresh;
134 | data['collect'] = this.collect;
135 | data['shareUser'] = this.shareUser;
136 | data['desc'] = this.desc;
137 | return data;
138 | }
139 |
140 | static List parseList(List list) {
141 | List articles = List();
142 | for (var a in list) {
143 | Article article = Article.fromJson(a);
144 | articles.add(article);
145 | }
146 | return articles;
147 | }
148 | }
149 |
150 | class ArticleTag {
151 | String name;
152 | String url;
153 |
154 | ArticleTag({this.name, this.url});
155 |
156 | ArticleTag.fromJson(Map json) {
157 | name = json['name'];
158 | url = json['url'];
159 | }
160 |
161 | Map toJson() {
162 | final Map data = new Map();
163 | data['name'] = this.name;
164 | data['url'] = this.url;
165 | return data;
166 | }
167 | }
168 |
--------------------------------------------------------------------------------
/lib/http/http_request.dart:
--------------------------------------------------------------------------------
1 | import 'dart:convert';
2 |
3 | import 'package:cookie_jar/cookie_jar.dart';
4 | import 'package:dio/dio.dart';
5 | import 'package:dio_cookie_manager/dio_cookie_manager.dart';
6 | import 'package:flutter/cupertino.dart';
7 | import 'package:flutter/material.dart';
8 | import 'package:wanandroidflutter/constant/Constants.dart';
9 | import 'package:wanandroidflutter/http/api.dart';
10 | import 'package:wanandroidflutter/http/base_response.dart';
11 | import 'package:wanandroidflutter/page/account/login_fragment.dart';
12 | import 'package:wanandroidflutter/utils/common.dart';
13 |
14 | class HttpRequest {
15 | static String _baseUrl = Api.BASE_URL;
16 | static HttpRequest instance;
17 |
18 | Dio dio;
19 | BaseOptions options;
20 |
21 | CancelToken cancelToken = CancelToken();
22 |
23 | static HttpRequest getInstance() {
24 | if (instance == null) {
25 | instance = HttpRequest();
26 | }
27 | return instance;
28 | }
29 |
30 | HttpRequest() {
31 | options = BaseOptions(
32 | // 访问url
33 | baseUrl: _baseUrl,
34 | // 连接超时时间
35 | connectTimeout: 5000,
36 | // 响应流收到数据的间隔
37 | receiveTimeout: 15000,
38 | // http请求头
39 | headers: {"version": "1.0.0"},
40 | // 接收响应数据的格式
41 | responseType: ResponseType.plain,
42 | );
43 | dio = Dio(options);
44 | // 在拦截其中加入Cookie管理器
45 | dio.interceptors.add(CookieManager(CookieJar()));
46 | }
47 |
48 | get(url,
49 | {data,
50 | options,
51 | cancelToken,
52 | BuildContext context,
53 | Function successCallBack,
54 | Function errorCallBack}) async {
55 | Response response;
56 | try {
57 | response = await dio.get(url,
58 | queryParameters: data, options: options, cancelToken: cancelToken);
59 | } on DioError catch (e) {
60 | handlerError(e);
61 | successCallBack(null);
62 | }
63 | if (response.data != null) {
64 | BaseResponse baseResponse =
65 | BaseResponse.fromJson(json.decode(response.data));
66 | if (baseResponse != null) {
67 | switch (baseResponse.errorCode) {
68 | case 0:
69 | successCallBack(jsonEncode(baseResponse.data));
70 | break;
71 | case -1001:
72 | /// 返回-1001跳转到登录页
73 | errorCallBack(baseResponse.errorCode, baseResponse.errorMessage);
74 | if (context != null) {
75 | CommonUtils.push(
76 | context,
77 | Scaffold(
78 | body: LoginPage(),
79 | ));
80 | }
81 | break;
82 | default:
83 | errorCallBack(baseResponse.errorCode, baseResponse.errorMessage);
84 | break;
85 | }
86 | } else {
87 | errorCallBack(Constants.NETWORK_JSON_EXCEPTION, "网络数据问题");
88 | }
89 | } else {
90 | errorCallBack(Constants.NETWORK_ERROR, "网络出错啦,请稍后重试");
91 | }
92 | }
93 |
94 | post(url,
95 | {data,
96 | options,
97 | cancelToken,
98 | BuildContext context,
99 | Function successCallBack,
100 | Function errorCallBack}) async {
101 | Response response;
102 | try {
103 | response = await dio.post(url,
104 | queryParameters: data, options: options, cancelToken: cancelToken);
105 | } on DioError catch (e) {
106 | handlerError(e);
107 | successCallBack(null);
108 | }
109 | if (response.data != null) {
110 | BaseResponse baseResponse =
111 | BaseResponse.fromJson(json.decode(response.data));
112 | if (baseResponse != null) {
113 | switch (baseResponse.errorCode) {
114 | case 0:
115 | successCallBack(jsonEncode(baseResponse.data));
116 | break;
117 | case -1001:
118 | errorCallBack(baseResponse.errorCode, baseResponse.errorMessage);
119 | if (context != null) {
120 | CommonUtils.push(
121 | context,
122 | Scaffold(
123 | body: LoginPage(),
124 | ));
125 | }
126 | break;
127 | default:
128 | errorCallBack(baseResponse.errorCode, baseResponse.errorMessage);
129 | break;
130 | }
131 | } else {
132 | errorCallBack(Constants.NETWORK_JSON_EXCEPTION, "网络数据问题");
133 | }
134 | } else {
135 | errorCallBack(Constants.NETWORK_ERROR, "网络出错啦,请稍后重试");
136 | }
137 | }
138 |
139 | handlerError(DioError e) {
140 | if (e.type == DioErrorType.CONNECT_TIMEOUT) {
141 | print("连接超时");
142 | } else if (e.type == DioErrorType.SEND_TIMEOUT) {
143 | print("请求超时");
144 | } else if (e.type == DioErrorType.RECEIVE_TIMEOUT) {
145 | print("响应超时");
146 | } else if (e.type == DioErrorType.RESPONSE) {
147 | print("响应异常");
148 | } else if (e.type == DioErrorType.CANCEL) {
149 | print("请求取消");
150 | } else {
151 | print("未知错误");
152 | }
153 | }
154 | }
155 |
--------------------------------------------------------------------------------
/lib/page/collect/collect_fragment.dart:
--------------------------------------------------------------------------------
1 | import 'dart:convert';
2 |
3 | import 'package:flutter/material.dart';
4 | import 'package:flutter_easyrefresh/easy_refresh.dart';
5 | import 'package:provider/provider.dart';
6 | import 'package:wanandroidflutter/application.dart';
7 | import 'package:wanandroidflutter/data/article.dart';
8 | import 'package:wanandroidflutter/http/api.dart';
9 | import 'package:wanandroidflutter/http/http_request.dart';
10 | import 'package:wanandroidflutter/page/input/share_fragment.dart';
11 | import 'package:wanandroidflutter/theme/theme_model.dart';
12 | import 'package:wanandroidflutter/utils/collect_event.dart';
13 | import 'package:wanandroidflutter/utils/common.dart';
14 | import 'package:wanandroidflutter/widget/collect_item.dart';
15 | import 'package:wanandroidflutter/widget/custom_refresh.dart';
16 | import 'package:wanandroidflutter/widget/page_widget.dart';
17 |
18 | class CollectFragment extends StatefulWidget {
19 | @override
20 | _CollectFragmentState createState() => _CollectFragmentState();
21 | }
22 |
23 | class _CollectFragmentState extends State
24 | with AutomaticKeepAliveClientMixin {
25 | List collectList = List();
26 | int currentPage = 0;
27 | ScrollController _scrollController;
28 | PageStateController _pageStateController;
29 | GlobalKey _easyRefreshKey =
30 | new GlobalKey();
31 |
32 | void _onRefresh(bool up) {
33 | if (up) {
34 | currentPage = 0;
35 | loadCollectList();
36 | } else {
37 | currentPage++;
38 | loadCollectList();
39 | }
40 | }
41 |
42 | @override
43 | void initState() {
44 | super.initState();
45 | _pageStateController = PageStateController();
46 | _scrollController = ScrollController();
47 | loadCollectList();
48 | Application.eventBus.on().listen((event) {
49 | setState(() {
50 | _onRefresh(true);
51 | });
52 | });
53 | }
54 |
55 | void loadCollectList() async {
56 | HttpRequest.getInstance().get("${Api.COLLECT_LIST}$currentPage/json",
57 | successCallBack: (data) {
58 | if (currentPage == 0) {
59 | collectList.clear();
60 | }
61 | _easyRefreshKey.currentState.callRefreshFinish();
62 | _easyRefreshKey.currentState.callLoadMoreFinish();
63 | if (data != null) {
64 | _pageStateController.changeState(PageState.LoadSuccess);
65 | Map dataJson = json.decode(data);
66 | List responseJson = json.decode(json.encode(dataJson["datas"]));
67 | setState(() {
68 | collectList
69 | .addAll(responseJson.map((m) => Article.fromJson(m)).toList());
70 | });
71 | if (collectList.length == 0) {
72 | _pageStateController.changeState(PageState.NoData);
73 | }
74 | } else {
75 | _pageStateController.changeState(PageState.LoadFail);
76 | }
77 | }, errorCallBack: (code, msg) {});
78 | }
79 |
80 | @override
81 | Widget build(BuildContext context) {
82 | var appTheme = Provider.of(context);
83 | return Scaffold(
84 | body: PageWidget(
85 | controller: _pageStateController,
86 | reload: () {
87 | loadCollectList();
88 | },
89 | child: CustomRefresh(
90 | easyRefreshKey: _easyRefreshKey,
91 | onRefresh: () {
92 | _onRefresh(true);
93 | },
94 | loadMore: () {
95 | _onRefresh(false);
96 | },
97 | child: ListView.builder(
98 | controller: _scrollController,
99 | itemCount: collectList.length,
100 | itemBuilder: (context, index) {
101 | return CollectWidget(collectList[index]);
102 | })),
103 | ),
104 | floatingActionButton: FloatingActionButton(
105 | backgroundColor: appTheme.themeColor.withAlpha(180),
106 | child: Icon(Icons.add),
107 | onPressed: () {
108 | _inAddCollect();
109 | }),
110 | );
111 | }
112 |
113 | void _inAddCollect() {
114 | showDialog(
115 | context: context,
116 | barrierDismissible: false, // user must tap button!
117 | builder: (BuildContext context) {
118 | return SimpleInputDialogLayout(
119 | isCollectArticle: true,
120 | confirmCallback1: (collectTitle, collectAuthor, collectUrl) async {
121 | //收藏文章
122 | var data = {
123 | 'title': collectTitle,
124 | 'author': collectAuthor,
125 | 'link': collectUrl
126 | };
127 | HttpRequest.getInstance().post(Api.ADD_COLLECT_ARTICLE,
128 | data: data, successCallBack: (data) {
129 | CommonUtils.toast("收藏文章成功");
130 | Application.eventBus.fire(CollectEvent());
131 | });
132 | },
133 | );
134 | });
135 | }
136 |
137 | @override
138 | bool get wantKeepAlive => true;
139 | }
140 |
--------------------------------------------------------------------------------
/lib/page/wechat/wechat_list_fragment.dart:
--------------------------------------------------------------------------------
1 | import 'dart:convert';
2 |
3 | import 'package:flutter/material.dart';
4 | import 'package:flutter_easyrefresh/easy_refresh.dart';
5 | import 'package:provider/provider.dart';
6 | import 'package:wanandroidflutter/application.dart';
7 | import 'package:wanandroidflutter/data/article.dart';
8 | import 'package:wanandroidflutter/http/http_request.dart';
9 | import 'package:wanandroidflutter/http/api.dart';
10 | import 'package:wanandroidflutter/theme/theme_model.dart';
11 | import 'package:wanandroidflutter/utils/collect_event.dart';
12 | import 'package:wanandroidflutter/utils/login_event.dart';
13 | import 'package:wanandroidflutter/utils/loginout_event.dart';
14 | import 'package:wanandroidflutter/widget/article_item.dart';
15 | import 'package:wanandroidflutter/widget/custom_refresh.dart';
16 | import 'package:wanandroidflutter/widget/page_widget.dart';
17 |
18 | class WeChatListFragment extends StatefulWidget {
19 | int _Id;
20 |
21 | WeChatListFragment(this._Id);
22 |
23 | @override
24 | WeChatListFragmentState createState() => WeChatListFragmentState(_Id);
25 | }
26 |
27 | class WeChatListFragmentState extends State
28 | with AutomaticKeepAliveClientMixin {
29 | int _Id;
30 | int currentPage = 1;
31 | List weChatArticleList = List();
32 |
33 | WeChatListFragmentState(this._Id);
34 |
35 | ScrollController _scrollController;
36 | PageStateController _pageStateController;
37 |
38 | bool isShowFab = false;
39 |
40 | GlobalKey _easyRefreshKey =
41 | new GlobalKey();
42 |
43 | @override
44 | void initState() {
45 | super.initState();
46 | _pageStateController = PageStateController();
47 | _scrollController = ScrollController();
48 | loadWeChatArticleList();
49 | Application.eventBus.on().listen((event) {
50 | _onRefresh(true);
51 | });
52 | Application.eventBus.on().listen((event) {
53 | _onRefresh(true);
54 | });
55 | Application.eventBus.on().listen((event) {
56 | _onRefresh(true);
57 | });
58 | initFabAnimator();
59 | }
60 |
61 | void initFabAnimator() {
62 | _scrollController.addListener(() {
63 | if (_scrollController.offset < 200) {
64 | setState(() {
65 | isShowFab = false;
66 | });
67 | } else if (_scrollController.offset >= 200) {
68 | setState(() {
69 | isShowFab = true;
70 | });
71 | }
72 | });
73 | }
74 |
75 | void loadWeChatArticleList() async {
76 | HttpRequest.getInstance().get("${Api.WECHAT_LIST}$_Id/$currentPage/json",
77 | successCallBack: (data) {
78 | if (currentPage == 1) {
79 | weChatArticleList.clear();
80 | }
81 | _easyRefreshKey.currentState.callRefreshFinish();
82 | _easyRefreshKey.currentState.callLoadMoreFinish();
83 | if (data != null) {
84 | _pageStateController.changeState(PageState.LoadSuccess);
85 | Map dataJson = json.decode(data);
86 | List responseJson = json.decode(json.encode(dataJson["datas"]));
87 | print(responseJson);
88 | setState(() {
89 | weChatArticleList
90 | .addAll(responseJson.map((m) => Article.fromJson(m)).toList());
91 | });
92 | if (weChatArticleList.length == 0) {
93 | _pageStateController.changeState(PageState.NoData);
94 | }
95 | } else {
96 | _pageStateController.changeState(PageState.LoadFail);
97 | }
98 | }, errorCallBack: (code, msg) {});
99 | }
100 |
101 | void _onRefresh(bool up) {
102 | if (up) {
103 | currentPage = 1;
104 | loadWeChatArticleList();
105 | } else {
106 | currentPage++;
107 | loadWeChatArticleList();
108 | }
109 | }
110 |
111 | @override
112 | Widget build(BuildContext context) {
113 | var appTheme = Provider.of(context);
114 | return Scaffold(
115 | body: PageWidget(
116 | controller: _pageStateController,
117 | reload: () {
118 | loadWeChatArticleList();
119 | },
120 | child: CustomRefresh(
121 | easyRefreshKey: _easyRefreshKey,
122 | onRefresh: () {
123 | _onRefresh(true);
124 | },
125 | loadMore: () {
126 | _onRefresh(false);
127 | },
128 | child: ListView.builder(
129 | controller: _scrollController,
130 | itemCount: weChatArticleList.length,
131 | itemBuilder: (context, index) {
132 | return ArticleWidget(weChatArticleList[index]);
133 | })),
134 | ),
135 | floatingActionButton: isShowFab
136 | ? FloatingActionButton(
137 | backgroundColor: appTheme.themeColor.withAlpha(180),
138 | child: Icon(Icons.arrow_upward),
139 | onPressed: () {
140 | _scrollController.animateTo(0,
141 | duration: Duration(milliseconds: 1000),
142 | curve: Curves.linear);
143 | })
144 | : null,
145 | );
146 | }
147 |
148 | @override
149 | // TODO: implement wantKeepAlive
150 | bool get wantKeepAlive => true;
151 | }
152 |
--------------------------------------------------------------------------------
/lib/page/project/project_list_fragment.dart:
--------------------------------------------------------------------------------
1 | import 'dart:convert';
2 |
3 | import 'package:flutter/material.dart';
4 | import 'package:flutter_easyrefresh/easy_refresh.dart';
5 | import 'package:provider/provider.dart';
6 | import 'package:wanandroidflutter/application.dart';
7 | import 'package:wanandroidflutter/data/article.dart';
8 | import 'package:wanandroidflutter/http/http_request.dart';
9 | import 'package:wanandroidflutter/http/api.dart';
10 | import 'package:wanandroidflutter/theme/theme_model.dart';
11 | import 'package:wanandroidflutter/utils/collect_event.dart';
12 | import 'package:wanandroidflutter/utils/login_event.dart';
13 | import 'package:wanandroidflutter/utils/loginout_event.dart';
14 | import 'package:wanandroidflutter/widget/article_item.dart';
15 | import 'package:wanandroidflutter/widget/custom_refresh.dart';
16 | import 'package:wanandroidflutter/widget/page_widget.dart';
17 |
18 | class ProjectListFragment extends StatefulWidget {
19 | int _Id;
20 |
21 | ProjectListFragment(this._Id);
22 |
23 | @override
24 | State createState() {
25 | return ProjectListFragmentState(_Id);
26 | }
27 | }
28 |
29 | class ProjectListFragmentState extends State
30 | with AutomaticKeepAliveClientMixin {
31 | int _Id;
32 | int currentPage = 1;
33 | List projectArticleList = List();
34 |
35 | ProjectListFragmentState(this._Id);
36 |
37 | ScrollController _scrollController;
38 | PageStateController _pageStateController;
39 | bool isShowFab = false;
40 |
41 | GlobalKey _easyRefreshKey =
42 | new GlobalKey();
43 |
44 | @override
45 | void initState() {
46 | super.initState();
47 | _pageStateController = PageStateController();
48 | _scrollController = ScrollController();
49 | loadprojectArticleList();
50 | Application.eventBus.on().listen((event) {
51 | _onRefresh(true);
52 | });
53 | Application.eventBus.on().listen((event) {
54 | _onRefresh(true);
55 | });
56 | Application.eventBus.on().listen((event) {
57 | _onRefresh(true);
58 | });
59 | initFabAnimator();
60 | }
61 |
62 | void initFabAnimator() {
63 | _scrollController.addListener(() {
64 | if (_scrollController.offset < 200) {
65 | setState(() {
66 | isShowFab = false;
67 | });
68 | } else if (_scrollController.offset >= 200) {
69 | setState(() {
70 | isShowFab = true;
71 | });
72 | }
73 | });
74 | }
75 |
76 | void loadprojectArticleList() async {
77 | HttpRequest.getInstance()
78 | .get("${Api.PROJECT_LIST}$currentPage/json?cid=$_Id",
79 | successCallBack: (data) {
80 | if (currentPage == 1) {
81 | projectArticleList.clear();
82 | }
83 | _easyRefreshKey.currentState.callRefreshFinish();
84 | _easyRefreshKey.currentState.callLoadMoreFinish();
85 | if (data != null) {
86 | _pageStateController.changeState(PageState.LoadSuccess);
87 | Map dataJson = json.decode(data);
88 | List responseJson = json.decode(json.encode(dataJson["datas"]));
89 | print(responseJson);
90 | setState(() {
91 | projectArticleList
92 | .addAll(responseJson.map((m) => Article.fromJson(m)).toList());
93 | });
94 | if (projectArticleList.length == 0) {
95 | _pageStateController.changeState(PageState.NoData);
96 | }
97 | } else {
98 | _pageStateController.changeState(PageState.LoadFail);
99 | }
100 | }, errorCallBack: (code, msg) {});
101 | }
102 |
103 | void _onRefresh(bool up) {
104 | if (up) {
105 | currentPage = 1;
106 | loadprojectArticleList();
107 | } else {
108 | currentPage++;
109 | loadprojectArticleList();
110 | }
111 | }
112 |
113 | @override
114 | Widget build(BuildContext context) {
115 | var appTheme = Provider.of(context);
116 | return Scaffold(
117 | body: PageWidget(
118 | controller: _pageStateController,
119 | reload: () {
120 | loadprojectArticleList();
121 | },
122 | child: CustomRefresh(
123 | easyRefreshKey: _easyRefreshKey,
124 | onRefresh: () {
125 | _onRefresh(true);
126 | },
127 | loadMore: () {
128 | _onRefresh(false);
129 | },
130 | child: ListView.builder(
131 | controller: _scrollController,
132 | itemCount: projectArticleList.length,
133 | itemBuilder: (context, index) {
134 | return ArticleWidget(projectArticleList[index]);
135 | })),
136 | ),
137 | floatingActionButton: isShowFab
138 | ? FloatingActionButton(
139 | backgroundColor: appTheme.themeColor.withAlpha(180),
140 | child: Icon(Icons.arrow_upward),
141 | onPressed: () {
142 | _scrollController.animateTo(0,
143 | duration: Duration(milliseconds: 1000),
144 | curve: Curves.linear);
145 | })
146 | : null,
147 | );
148 | }
149 |
150 | @override
151 | // TODO: implement wantKeepAlive
152 | bool get wantKeepAlive => true;
153 | }
154 |
--------------------------------------------------------------------------------
/lib/generated/intl/messages_en.dart:
--------------------------------------------------------------------------------
1 | // DO NOT EDIT. This is code generated via package:intl/generate_localized.dart
2 | // This is a library that provides messages for a en locale. All the
3 | // messages from the main program should be duplicated here with the same
4 | // function name.
5 |
6 | // Ignore issues from commonly used lints in this file.
7 | // ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new
8 | // ignore_for_file:prefer_single_quotes,comment_references, directives_ordering
9 | // ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases
10 | // ignore_for_file:unused_import, file_names
11 |
12 | import 'package:intl/intl.dart';
13 | import 'package:intl/message_lookup_by_library.dart';
14 |
15 | final messages = new MessageLookup();
16 |
17 | typedef String MessageIfAbsent(String messageStr, List args);
18 |
19 | class MessageLookup extends MessageLookupByLibrary {
20 | String get localeName => 'en';
21 |
22 | final messages = _notInlinedMessages(_notInlinedMessages);
23 | static _notInlinedMessages(_) => {
24 | "about" : MessageLookupByLibrary.simpleMessage("About"),
25 | "autoBySystem" : MessageLookupByLibrary.simpleMessage("Auto"),
26 | "cancel" : MessageLookupByLibrary.simpleMessage("Cancel"),
27 | "clear_cache" : MessageLookupByLibrary.simpleMessage("ClearCache"),
28 | "clear_cache_tip" : MessageLookupByLibrary.simpleMessage("Are you sure?"),
29 | "clear_history" : MessageLookupByLibrary.simpleMessage("Clear"),
30 | "collect" : MessageLookupByLibrary.simpleMessage("Collect"),
31 | "complete_refresh" : MessageLookupByLibrary.simpleMessage("complete"),
32 | "confirm" : MessageLookupByLibrary.simpleMessage("Confirm"),
33 | "copy_link" : MessageLookupByLibrary.simpleMessage("CopyLink"),
34 | "copy_tip" : MessageLookupByLibrary.simpleMessage("CopySuccess"),
35 | "crop_image" : MessageLookupByLibrary.simpleMessage("Crop image"),
36 | "down_refresh" : MessageLookupByLibrary.simpleMessage("Down refresh"),
37 | "exit" : MessageLookupByLibrary.simpleMessage("Exit"),
38 | "hot_search" : MessageLookupByLibrary.simpleMessage("HotSearch"),
39 | "input_search" : MessageLookupByLibrary.simpleMessage("Input search key"),
40 | "integral" : MessageLookupByLibrary.simpleMessage("Integral: "),
41 | "integral_rank" : MessageLookupByLibrary.simpleMessage("ranking"),
42 | "kuaile_font" : MessageLookupByLibrary.simpleMessage("MiaoQu"),
43 | "language_setting" : MessageLookupByLibrary.simpleMessage("Language"),
44 | "level" : MessageLookupByLibrary.simpleMessage("level: "),
45 | "login" : MessageLookupByLibrary.simpleMessage("Login"),
46 | "login_tip" : MessageLookupByLibrary.simpleMessage("Click avatar login"),
47 | "loginout" : MessageLookupByLibrary.simpleMessage("LoginOut"),
48 | "loginoutTip" : MessageLookupByLibrary.simpleMessage("Loginout Success"),
49 | "me_collect" : MessageLookupByLibrary.simpleMessage("MeCollect"),
50 | "me_share" : MessageLookupByLibrary.simpleMessage("MeShare"),
51 | "my_blog" : MessageLookupByLibrary.simpleMessage("Blog"),
52 | "network_error" : MessageLookupByLibrary.simpleMessage("Network error, please try again!"),
53 | "new_article" : MessageLookupByLibrary.simpleMessage("New"),
54 | "night_mode" : MessageLookupByLibrary.simpleMessage("NightMode"),
55 | "no_data" : MessageLookupByLibrary.simpleMessage("No data, please try again!"),
56 | "normol_font" : MessageLookupByLibrary.simpleMessage("Normal"),
57 | "password" : MessageLookupByLibrary.simpleMessage("Input password"),
58 | "rank" : MessageLookupByLibrary.simpleMessage("Ranking"),
59 | "refreshing" : MessageLookupByLibrary.simpleMessage("refreshing"),
60 | "register" : MessageLookupByLibrary.simpleMessage("Register"),
61 | "release_refresh" : MessageLookupByLibrary.simpleMessage("release refresh"),
62 | "repassword" : MessageLookupByLibrary.simpleMessage("Confirm password"),
63 | "search" : MessageLookupByLibrary.simpleMessage("go"),
64 | "search_history" : MessageLookupByLibrary.simpleMessage("SearchHistory"),
65 | "search_tip" : MessageLookupByLibrary.simpleMessage("Input Empty"),
66 | "setting" : MessageLookupByLibrary.simpleMessage("Setting"),
67 | "share_to_square" : MessageLookupByLibrary.simpleMessage("Share"),
68 | "square" : MessageLookupByLibrary.simpleMessage("Square"),
69 | "stranger" : MessageLookupByLibrary.simpleMessage("Stranger"),
70 | "switching_fonts" : MessageLookupByLibrary.simpleMessage("Fonts"),
71 | "tab_home" : MessageLookupByLibrary.simpleMessage("Home"),
72 | "tab_navigation" : MessageLookupByLibrary.simpleMessage("Navigation"),
73 | "tab_project" : MessageLookupByLibrary.simpleMessage("Project"),
74 | "tab_system" : MessageLookupByLibrary.simpleMessage("System"),
75 | "tab_wechat" : MessageLookupByLibrary.simpleMessage("WX"),
76 | "theme" : MessageLookupByLibrary.simpleMessage("Theme"),
77 | "theme_choose" : MessageLookupByLibrary.simpleMessage("Theme Choose"),
78 | "theme_tips" : MessageLookupByLibrary.simpleMessage("Cannot be changed in night mode~"),
79 | "top" : MessageLookupByLibrary.simpleMessage("Top"),
80 | "up_refresh" : MessageLookupByLibrary.simpleMessage("Up refresh"),
81 | "update_time" : MessageLookupByLibrary.simpleMessage("update time: "),
82 | "username" : MessageLookupByLibrary.simpleMessage("Input username"),
83 | "wenda" : MessageLookupByLibrary.simpleMessage("Q&A")
84 | };
85 | }
86 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | # WanAndroid-Flutter 项目
3 | 🔥🔥🔥项目基于 Flutter 移动应用框架,采用 Dart 语言编写,打造新的WanAndroid,持续更新(♡˙♡)....
4 | [WanAndroid(github)](https://github.com/wangjianxiandev/WanAndroidFlutter) 如果项目对你有帮助,留下个star再走叭(๑• . •๑)
5 |
6 | ## 下载体验
7 |
8 |
9 |  |
10 |
11 |
12 |
13 | #### 密码123456
14 | ## 当前版本(2.0)
15 | - 添加自定义设置头像功能
16 | - 添加切换字体功能
17 | - 添加切换语言功能
18 | ## 项目展示
19 |
20 | 
21 |
22 |
23 |  |
24 |  |
25 |  |
26 |
27 |
28 |
29 |
30 |  |
31 |  |
32 |  |
33 |
34 |
35 |
41 |
42 |
43 |  |
44 |  |
45 |  |
46 |
47 |
48 |
49 |
50 |  |
51 |  |
52 |  |
53 |
54 |
55 |
56 | ## 项目功能
57 |
58 | ### 首页
59 | - 首页文章列表
60 | - 首页banner
61 | - 搜索热词(包含在搜索界面)
62 | - 置顶文章
63 | ### 广场
64 | - 我的分享
65 | - 分享文章
66 |
67 | ### 知识体系
68 | - 体系数据
69 | - 知识体系下的文章
70 |
71 | ### 导航
72 | - 导航数据
73 |
74 | ### 公众号
75 | - 获取公众号列表
76 |
77 | ### 项目
78 | - 项目分类
79 | - 项目列表数据
80 |
81 | ### 登录与注册
82 | - 登录、注册功能
83 |
84 | ### 收藏
85 | - 收藏文章列表
86 | - 取消收藏
87 |
88 | ### 搜索
89 | - 首页文章搜索
90 | - 关键词搜索
91 | - 搜索历史记录
92 |
93 | ### 设置
94 | - 清除缓存
95 | - 夜间模式
96 | - 切换字体
97 | - 版本信息
98 | - 关于我们
99 | - 更换主题
100 | - 退出登录
101 |
102 |
103 | ### 特别感谢
104 | - [感谢鸿洋大神的WanAndroid网站提供开放Api](https://www.wanandroid.com/)
105 | - [参考开源项目fun_wandroid中的部分动画UI ](https://github.com/phoenixsky/fun_android_flutter)
106 |
107 | ### 开源
108 |
109 | | 第三方库 | 释义 |
110 | | --- | --- |
111 | | dio | 网络请求 |
112 | | cookie_jar | 网络Cookie |
113 | | dio_cookie_manager | 网络Cookie管理 |
114 | | flutter_easyrefresh | 下拉刷新以及上拉加载 |
115 | | event_bus | 事件总线 |
116 | | shared_preferences | 本地配置文件存储 |
117 | | webview_flutter | webview |
118 | | provider | 跨组件数据共享 |
119 | | url_launcher |唤醒第三方应用 |
120 | | flutter_swiper | 轮播 |
121 | | image_picker | 图像选择 |
122 | | image_cropper| 图像裁剪 |
123 |
--------------------------------------------------------------------------------
/lib/widget/share_item.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | import 'package:provider/provider.dart';
4 | import 'package:wanandroidflutter/application.dart';
5 | import 'package:wanandroidflutter/data/article.dart';
6 | import 'package:wanandroidflutter/http/http_request.dart';
7 | import 'package:wanandroidflutter/page/webview_page.dart';
8 | import 'package:wanandroidflutter/theme/dark_model.dart';
9 | import 'package:wanandroidflutter/theme/theme_model.dart';
10 | import 'package:wanandroidflutter/utils/common.dart';
11 | import 'package:wanandroidflutter/utils/refresh_event.dart';
12 |
13 | import 'article_title.dart';
14 |
15 | //文章item
16 | class ShareWidget extends StatefulWidget {
17 | Article article;
18 |
19 | ShareWidget(this.article);
20 |
21 | @override
22 | State createState() {
23 | return _ShareWidgetState();
24 | }
25 | }
26 |
27 | class _ShareWidgetState extends State {
28 | Article article;
29 |
30 | @override
31 | Widget build(BuildContext context) {
32 | article = widget.article;
33 | var appTheme = Provider.of(context);
34 | var isDarkMode = Provider.of(context).isDark;
35 | return GestureDetector(
36 | onTap: () {
37 | CommonUtils.push(
38 | context,
39 | WebViewPage(
40 | url: article.link,
41 | title: article.title,
42 | id: article.id,
43 | isCollect: article.collect,
44 | ));
45 | },
46 | child: Card(
47 | elevation: 15.0,
48 | shape: const RoundedRectangleBorder(
49 | borderRadius: BorderRadius.all(Radius.circular(14.0))),
50 | margin: EdgeInsets.all(5),
51 | child: Container(
52 | padding: EdgeInsets.fromLTRB(10, 10, 10, 5),
53 | child: Column(
54 | children: [
55 | Row(
56 | children: [
57 | Expanded(
58 | flex: 1,
59 | child: Row(
60 | children: [
61 | Icon(
62 | article.author == ""
63 | ? Icons.folder_shared
64 | : Icons.person,
65 | size: 20.0,
66 | color: !isDarkMode
67 | ? appTheme.themeColor
68 | : Colors.white.withAlpha(120),
69 | ),
70 | Container(
71 | padding: EdgeInsets.only(left: 5),
72 | child: Text(
73 | "${article.author.isNotEmpty ? article.author : article.shareUser}",
74 | overflow: TextOverflow.ellipsis,
75 | maxLines: 1,
76 | style: Theme.of(context).textTheme.caption),
77 | ),
78 | ],
79 | )),
80 | Align(
81 | alignment: Alignment.centerRight,
82 | child: Row(
83 | children: