flutter_wanandroid

Introduction: 🔥🔥🔥 基于 Google Flutter 的 WanAndroid 客户端,支持 Android 和 iOS。包括 BLoC、RxDart 、国际化、主题色、启动页、引导页,拥有较好的项目结构&比较规范的代码!
More: Author   ReportBugs   
Tags:

本项目包含启动页,引导页,主题色,国际化,Bloc,RxDart。拥有较好的项目结构,比较规范的代码。 App 拥有精致的 UI 界面,统一的交互,侧滑退出,列表和 Web 界面均提供快速滚动至顶部功能。

有关项目最新动态,可以关注 App 内第一条 Hot Item 信息。

运行本项目注意!!!

由于在国内访问 Flutter 有时可能会受到限制,clone 项目后,请勿直接 packages get,建议运行如下目录行:

export PUB_HOSTED_URL=https://pub.flutter-io.cn  
export FLUTTER_STORAGE_BASE_URL=https://storage.flutter-io.cn  
flutter packages get
flutter run --release

更新说明

v0.2.1 (2019.05.08)

1.新增登录/注册。
2.新增收藏功能。
3.一些优化~。

温馨提醒:
① 默认主题色修改为 deepPurpleAccent,与登录/注册页面元素保持一致。
② 设置新增升级提示次数,可关闭升级提醒,但超过 5 个版本未升级需要下载最新版。
③ 快速滚动至顶部按钮展示逻辑优化。

v0.2.0 (2019.03.29)

1.新增分享~。
2.新增网络状态页。

v0.1.9 (2019.03.16)

1.闪屏页支持视频。
2.支持 App 应用内升级。
3.玩安卓 Api 升级为 https。
4.Flutter Demos 新增 获取图片尺寸示例。

温馨提醒:
① 为了方便大家体验应用内升级,服务端版本号为:v0.2.0,Apk 版本始终为 v0.1.9。
② 由于 Apk 文件是放在 Github 上面的,可能下载速度会比较慢。
③ 为了保护掘金作者原创文章,热门文章修改为从第二页开始获取。

安卓 Apk

如需体验版本升级功能,可以下载旧版 apk。

点击下载 新版 v0.2.1 --- 旧版 v0.2.0

扫码下载 新版 v0.2.1
flutter_wanandroid

扫码下载 旧版 v0.2.0
flutter_wanandroid

iOS:请自行 clone 项目代码运行。

App 目录结构

  • |--lib
    • |-- blocs (bloc 相关)
    • |-- common (常用类,例如常量 Constant)
    • |-- data (网络数据层)
    • |-- db (数据库)
    • |-- demos (flutter demos)
    • |-- event (事件类)
    • |-- models (实体类)
    • |-- res (资源文件,string,colors,dimens,styles)
    • |-- ui (界面相关 page,dialog,widgets)
    • |-- utils (工具类)

data 网络数据层

  • |--data
    • |-- api (url 字段)
    • |-- net (单例 DioUtil)
    • |-- protocol (请求与返回实体类)
    • |-- repository (接口请求&解析)

api

class WanAndroidApi {
  /// 首页 banner http://www.wanandroid.com/banner/json
  static const String BANNER = "banner";
  static const String USER_REGISTER = "user/register"; //注册
  static const String USER_LOGIN = "user/login"; //登录
  static const String USER_LOGOUT = "user/logout"; //退出

  // 拼接 url
  static String getPath({String path: '', int page, String resType: 'json'}) {
    StringBuffer sb = new StringBuffer(path);
    if (page != null) {
      sb.write('/$page');
    }
    if (resType != null && resType.isNotEmpty) {
      sb.write('/$resType');
    }
    return sb.toString();
  }
}

网络请求工具类 单例DioUtil(基于 v1.0.13,仅供参考~)

// 打开 debug 模式.
DioUtil.openDebug();   

// 配置网络参数.
Options options = DioUtil.getDefOptions();
options.baseUrl = "http://www.wanandroid.com/";
HttpConfig config = new HttpConfig(options: options);
DioUtil().setConfig(config);

// 两种单例请求方式.
DioUtil().request<List>(Method.get, "banner/json");
DioUtil.getInstance().request(Method.get, "banner/json");

//示例
LoginReq req = new LoginReq('username', 'password');
DioUtil().request(Method.post, "user/login",data: req.toJson());

//示例
FormData formData = new FormData.from({
      "username": "username",
      "password": "password",
    });
DioUtil().requestR(Method.post, "user/login",data: rformData);

请求与返回实体类 protocol

class LoginReq {
  String username;
  String password;

  LoginReq(this.username, this.password);

  LoginReq.fromJson(Map<String, dynamic> json)
      : username = json['username'],
        password = json['password'];

  Map<String, dynamic> toJson() => {
    'username': username,
    'password': password,
  };

  @override
  String toString() {
    StringBuffer sb = new StringBuffer('{');
    sb.write("\"username\":\"$username\"");
    sb.write(",\"password\":$password");
    sb.write('}');
    return sb.toString();
  }
}

接口请求&解析 repository

 class WanRepository {
  Future<List<BannerModel>> getBanner() async {
    BaseResp<List> baseResp = await DioUtil().request<List>(
        Method.get, WanAndroidApi.getPath(path: WanAndroidApi.BANNER));
    List<BannerModel> bannerList;
    if (baseResp.code != Constant.STATUS_SUCCESS) {
      return new Future.error(baseResp.msg);
    }
    if (baseResp.data != null) {
      bannerList = baseResp.data.map((value) {
        return BannerModel.fromJson(value);
      }).toList();
    }
    return bannerList;
  }
}

资源文件 res

  • |--res
    • |-- colors.dart
    • |-- dimens.dart
    • |-- strings.dart
    • |-- styles.dart

colors.dart

class Colours {
  static const Color app_main = Color(0xFF666666);  

  static const Color text_dark = Color(0xFF333333);
  static const Color text_normal = Color(0xFF666666);
  static const Color text_gray = Color(0xFF999999);  
}

dimens.dart

class Dimens {
  static const double font_sp12 = 12;
  static const double font_sp14 = 14;
  static const double font_sp16 = 16;

  static const double gap_dp5 = 5;
  static const double gap_dp10 = 10;
}

strings.dart

class Ids {
  static const String titleHome = 'title_home';
}  
Map<String, Map<String, Map<String, String>>> localizedValues = {
  'en': {
    'US': {
      Ids.titleHome: 'Home',
    }
  },
  'zh': {
    'CN': {
      Ids.titleHome: '主页',
    },
    'HK': {
      Ids.titleHome: '主頁',
    },
    'TW': {
      Ids.titleHome: '主頁',
    }
  }
};

styles.dart

class TextStyles {
  static TextStyle listTitle = TextStyle(
    fontSize: Dimens.font_sp16,
    color: Colours.text_dark,
    fontWeight: FontWeight.bold,
  );
  static TextStyle listContent = TextStyle(
    fontSize: Dimens.font_sp14,
    color: Colours.text_normal,
  );
  static TextStyle listExtra = TextStyle(
    fontSize: Dimens.font_sp12,
    color: Colours.text_gray,
  );
}
//  间隔
class Gaps {
  // 水平间隔
  static Widget hGap5 = new SizedBox(width: Dimens.gap_dp5);
  static Widget hGap10 = new SizedBox(width: Dimens.gap_dp10);
  // 垂直间隔
  static Widget vGap5 = new SizedBox(height: Dimens.gap_dp5);
  static Widget vGap10 = new SizedBox(height: Dimens.gap_dp10);
}

Flutter 国际化相关 fluintl

fluintl 是一个为应用提供国际化的库,可快速集成实现应用多语言。该库封装了一个国际化支持类,通过提供统一方法 getString(id)获取字符串。

// 在 MyApp initState 配置多语言资源
setLocalizedValues(localizedValues); //配置多语言资源
// 在 MaterialApp 指定 localizationsDelegates 和 supportedLocales
MaterialApp(  
   home: MyHomePage(),  
   localizationsDelegates: [  
   GlobalMaterialLocalizations.delegate,  
   GlobalWidgetsLocalizations.delegate,  
   CustomLocalizations.delegate //设置本地化代理     
   ],  
   supportedLocales: CustomLocalizations.supportedLocales,//设置支持本地化语言集合     
); 
// 字符串获取
IntlUtil.getString(context, Ids.titleHome);
CustomLocalizations.of(context).getString(StringIds.titleHome);  

// 支持复用。替换字符串格式要求:'%\${index}\$s' ,{index} 第几个参数,从 0 开始。
Ids.click_times: '%\$0\$s 点击了%\$1\$s 次',   
IntlUtil.getString(context, Ids.click_times, params: ['Tom', '$_counter'])  
// print: Tom 点击了 0 次

Flutter 屏幕适配 ScreenUtil

方案一、不依赖 context

// 步骤 1
// 如果设计稿尺寸默认配置一致,无需该设置。  配置设计稿尺寸 默认 360.0 / 640.0 / 3.0
setDesignWHD(_designW,_designH,_designD);  

// 步骤 2
// 在 MainPageState build 调用 MediaQuery.of(context)
class MainPageState extends State<MainPage> {
  @override
  Widget build(BuildContext context) {
    // 在 MainPageState build 调用 MediaQuery.of(context)
    MediaQuery.of(context);
    return new Scaffold(
      appBar: new AppBar(),
    );
  }
}  

// 步骤 3
ScreenUtil.getInstance().screenWidth
ScreenUtil.getInstance().screenHeight
//屏幕适配相关  
ScreenUtil.getInstance().getWidth(size); //返回根据屏幕宽适配后尺寸(单位 dp or pt)
ScreenUtil.getInstance().getHeight(size); //返回根据屏幕高适配后尺寸 (单位 dp or pt)
ScreenUtil.getInstance().getWidthPx(sizePx); //sizePx 单位 px
ScreenUtil.getInstance().getHeightPx(sizePx); //sizePx 单位 px
ScreenUtil.getInstance().getSp(fontSize); //返回根据屏幕宽适配后字体尺寸

方案二、依赖 context

//如果设计稿尺寸默认配置一致,无需该设置。  配置设计稿尺寸 默认 360.0 / 640.0 / 3.0
setDesignWHD(_designW,_designH,_designD);  

ScreenUtil.getScreenW(context); //屏幕 宽
ScreenUtil.getScreenH(context); //屏幕 高
//屏幕适配相关  
ScreenUtil.getScaleW(context, size); //返回根据屏幕宽适配后尺寸(单位 dp or pt)
ScreenUtil.getScaleH(context, size); //返回根据屏幕高适配后尺寸 (单位 dp or pt)
ScreenUtil.getScaleSp(context, size) ;//返回根据屏幕宽适配后字体尺寸

Flutter 数据存储 SpUtil

单例"同步" SharedPreferences 工具类。
支持 get 获取默认参数、支持存储实体对象、支持存储实体对象数组。

    // 等待 Sp 初始化完成。
    await SpUtil.getInstance();

    SpUtil.getString('key', defValue: '');
    SpUtil.getInt('key', defValue: 0);

    /// save object example.
    /// 存储实体对象示例。
    City city = new City();
    city.name = "成都市";
    SpUtil.putObject("loc_city", city);

    Map dataStr = SpUtil.getObject("loc_city");
    City hisCity = dataStr == null ? null : City.fromJson(dataStr);
    print("thll Str: " + (hisCity == null ? "null" : hisCity.toString()));

    /// save object list example.
    /// 存储实体对象 List 示例。
    List<City> list = new List();
    list.add(new City(name: "成都市"));
    list.add(new City(name: "北京市"));
    SpUtil.putObjectList("loc_city_list", list);

    List<Map> dataList = SpUtil.getObjectList("loc_city_list");
    List<City> _cityList = dataList?.map((value) {
      return City.fromJson(value);
    })?.toList();

    print("thll List: " + (_cityList == null ? "null" : _cityList.toString()));

Flutter Demos

  • |--demos
    • |-- city_select_page.dart 城市列表(索引&悬停)示例
    • |-- date_page.dart 日期格式化示例
    • |-- image_size_page.dart 获取图片尺寸示例
    • |-- money_page.dart 金额(元转分/分转元)示例
    • |-- pinyin_page.dart 汉字转拼音示例
    • |-- regex_page.dart 正则工具类示例
    • |-- round_portrait_page.dart 圆形圆角头像示例
    • |-- timeline_page.dart 时间轴示例
    • |-- timer_page.dart 倒计时/定时任务示例
    • |-- widget_page.dart 获取 Widget 尺寸/屏幕坐标示例

Screenshot

主界面

启动页

侧滑 Back

快速滚动到顶部

分类页面

国际化

主题色

闪屏广告页

引导页

Big Thanks

① 感谢鸿洋大佬提供的开源 api
② 界面参考gitme
Github Trending Api
Streams-Block-Reactive-Programming-in-Flutter

开源库

  1. Dart 常用工具类库common_utils(作者)
    该库包含 TimerUtil(倒计时,定时任务),TimelineUtil(时间轴),DateUtil(日期格式化),RegexUtil(正则验证手机号,身份证,邮箱等等),RegexUtil(正则验证手机号,身份证,邮箱等等),NumUtil(保留 x 位小数, 精确加、减、乘、除, 防止精度丢失),MoneyUtil(元转分,分转元),ObjectUtil(判空),LogUtil(简单封装打印日志)。
    如果你有不错的纯 dart 工具类或对已有对工具类有更好的优化建议,欢迎 PR,大家一起维护~
  2. Flutter 常用工具类库flustars(作者)
    该库包含 SpUtil(单例"同步" SharedPreferences 工具类),ScreenUtil(屏幕适配),WidgetUtil(Widget 渲染监听,获取 Widget 宽高,在屏幕上的坐标)。
    如果你有不错的 Flutter 工具类或对已有对工具类有更好的优化建议,欢迎 PR,大家一起维护~
  3. 汉字转拼音库lpinyin(作者)
  4. 国际化/多语言库fluintl(作者)
  5. UI 组件库flukit
  6. 网络请求dio
  7. 图片加载cached_network_image
  8. 上拉加载和下拉刷新pull_to_refresh
  9. url_launcher
  10. 点赞效果LikeButton
  11. 安卓 webview 增加滚动监听webview_flutter
  12. 城市列表azlistview(作者)

项目问题汇总

Q1:Flutter 国际化系统切换 iOS 不生效问题?
A1:在 Xcode 项目 Localizations 下添加支持语言即可,原文

关于作者

GitHub : Sky24n
简书     : Sky24n
掘金     : Sky24n
Pub      : Sky24n
Email   : 863764940@qq.com
⭐⭐⭐ 如果您觉得本项目不错的话,来个 star 支持下作者吧! ⭐⭐⭐
关于项目任何问题请提交issues,私发 QQ 邮件将不再回复~

Flutter 版玩安卓 flutter_wanandroid

GitHub stars GitHub forks GitHub watchers

Flutter 仿滴滴出行 GreenTravel

GitHub stars GitHub forks GitHub watchers

Flutter 常用工具类库 flustars

GitHub stars GitHub forks GitHub watchers

Dart 常用工具类库 common_utils

GitHub stars GitHub forks GitHub watchers

Flutter 城市列表 azlistview

GitHub stars GitHub forks GitHub watchers

Flutter 汉字转拼音库 lpinyin

GitHub stars GitHub forks GitHub watchers

Flutter 国际化库 fluintl

GitHub stars GitHub forks GitHub watchers

Support Me
Apps
About Me
Google+: Trinea trinea
GitHub: Trinea