Android

完整app -- Flutter_Eyepetizer学习

2021-11-11  本文已影响0人  客观开发者

Eyepetizer kotlin 版本出来之后,很多人开始 flutter 上学习。
学习代码了。
clone 地址
https://hub.fastgit.org/fmtjava/Flutter_Eyepetizer
android studio 下载flutter 和dart 插件
在android setting 中配置flutter sdk 文件导入

image.png

导入文件夹就可以了。
然后进入main.dart 或pubspec.yaml中
点击


image.png

进行中


image.png
结果一直处于上面的结果中。。。
进入sdk 中bin 中有个缓存文件,进行删除

然后就好了


image.png

我本地的sdk 较低了,下载一份新的就。。。

image.png

但是timeout 了。。。
找到windows 设置中。开启


image.png
image.png

终于没有报错了
先看 源码 整体的框架和里面的 用到东西吧。


function.png technology.png

从图片中找到里面有 俩个说明 功能和核心技术框架。。
代码里面
component 是功能方法。
module 是组件化的一部分。。这个是第一次看到。。。

pubspec.yaml 里面可以配置使用。


  #组件化配置
  module_common:
    path: ./module/module_common
  module_home:
    path: ./module/module_home
  module_discover:
    path: ./module/module_discover
  module_hot:
    path: ./module/module_hot
  module_person:
    path: ./module/module_person
  module_detail:
    path: ./module/module_detail
  module_author:
    path: ./module/module_author

使用浏览器 去测试和开发以及浏览会快一点。


image.png image.png

很遗憾的是api 404 了


image.png

代码模块

启动

void main() {
  runApp(App());
  //Flutter沉浸式状态栏
  if (Platform.isAndroid) {
    SystemChrome.setSystemUIOverlayStyle(
        SystemUiOverlayStyle(statusBarColor: Colors.transparent));
  }
}

FutureBuilder 的使用。。
在不同的执行状态就可以加载不同的组件信息
FutureBuilder的三个子属性分别是
1.future 获取用户异步处理获得数据的代码
2.initialData: 初始化数据加载
3.builder 回调函数,暴露异步处理中的快照。这个是我们构建组件的主要组成。

首页TabNavigation
WillPopScope 的使用
https://zhuanlan.zhihu.com/p/140235529
onWillPop 可以退出app提示
child 的使用

PageView 是首页中用到的。viewpager 切换 view 用的。。。

bottomNavigationBar 的使用
BottomNavigationBar

bottomNavigationBar: ProviderWidget<TabNavigationModel>(
              model: TabNavigationModel(),
              builder: (context, model, child) {
                return BottomNavigationBar(
                  currentIndex: model.currentIndex,
                  onTap: (index) {
                    if (model.currentIndex != index) {
                      _pageController.jumpToPage(index);
                      model.changeBottomTabIndex(index);
                    }
                  },
                  type: BottomNavigationBarType.fixed,
                  //显示标题
                  items: [
                    _bottomItem(
                        DString.daily_paper,
                        'images/ic_home_normal.png',
                        'images/ic_home_selected.png'),
                    _bottomItem(
                        DString.discover,
                        'images/ic_discovery_normal.png',
                        'images/ic_discovery_selected.png'),
                    _bottomItem(DString.hot, 'images/ic_hot_normal.png',
                        'images/ic_hot_selected.png'),
                    _bottomItem(DString.mime, 'images/ic_mine_normal.png',
                        'images/ic_mine_selected.png')
                  ],
                  selectedItemColor: Color(0xff000000),
                  unselectedItemColor: Color(0xff9a9a9a),
                );
              }),

分装好了view 中点击事件的处理。
底部item 的处理。。。

_bottomItem(String title, String normalIcon, String selectIcon) {
    return BottomNavigationBarItem(
        icon: Image.asset(normalIcon, width: 24, height: 24),
        activeIcon: Image.asset(selectIcon, width: 24, height: 24),
        label: title);
  }

首页完成

第一个tab ..首页 HomePage
用ProviderWidget 和自定义widget 来封装一些view 。。
例如
//通用分页State封装
abstract class BaseListState<L, M extends PagingListModel<L, PagingModel<L>>,
使用mvvm 模式,自己写不一定会,用就感觉很容易了。

LoadingContainer 的封装,暂无数据的界面

就是我们看的首页的样子了。

Widget get _errorView {
    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          Image.asset(
            'images/ic_error.png',
            width: 100,
            height: 100,
            package: 'lib_ui',
          ),
          Padding(
            padding: const EdgeInsets.only(top: 8),
            child: Text(
              net_error_tip,
              style: TextStyle(color:hitTextColor, fontSize: 18),
            ),
          ),
          Padding(
            padding: const EdgeInsets.only(top: 8),
            child: OutlinedButton(
              onPressed: () => retry.call(),
              child: Text(
                reload_again,
                style: TextStyle(color: Colors.black87),
              ),
              style: ButtonStyle(
                  backgroundColor: MaterialStateProperty.all(Colors.white),
                  overlayColor: MaterialStateProperty.all(Colors.black12)),
            ),
          )
        ],
      ),
    );
  }

错误界面。。

return LoadingContainer(
              viewState: model.viewState,
              retry: model.retry,
              child: Container(
                  color: Colors.white,
                  child: SmartRefresher(
                      controller: model.refreshController,
                      onRefresh: model.refresh,
                      onLoading: model.loadMore,
                      enablePullUp: true,
                      enablePullDown: enablePullDown,
                      child: getContentChild(model))));
        });

下拉刷新界面SmartRefresher
从零开始的Flutter之旅: Provider 的学习。。
这些都是provider 需要看。

从零开始的Flutter之旅: StatelessWidget

从零开始的Flutter之旅: StatefulWidget

从零开始的Flutter之旅: InheritedWidget

provider 的中状态获取网络的情况都是通过他传到view 里面的。。

Flutter 状态管理 Provider的使用和实现MVVM
https://www.wodecun.com/blog/8069.html

SmartRefresher 的使用

pull_to_refresh: 1.6.3

OpenContainer 的
Flutter 插件:((OpenContainer Material Design 设计风格中的容器转换过渡)
https://blog.csdn.net/weixin_44819566/article/details/110954855

Fluttertoast的使用

void showTip(String tipMessage) {
  Fluttertoast.showToast(
      msg: tipMessage,
      toastLength: Toast.LENGTH_SHORT,
      gravity: ToastGravity.CENTER);
}

void showError(String errorMessage) {
  Fluttertoast.showToast(
      msg: errorMessage,
      toastLength: Toast.LENGTH_SHORT,
      gravity: ToastGravity.CENTER,
      backgroundColor: Colors.red,
      textColor: Colors.white);
}

网络获取数据http 使用的。。

只写了俩个方法。

import 'package:http/http.dart' as http;
import 'dart:convert';

class HttpManager {
  static Utf8Decoder utf8decoder = Utf8Decoder();

  //网络请求封装,通过方法回调执行的结果(成功或失败)
  //网络请求封装方式一:采用回调函数处理请求结果,类似Android开发
  static getData(String url,
      {Map<String, String> headers,
      Function success,
      Function fail,
      Function complete}) async {
    try {
      var response = await http.get(Uri.parse(url), headers: headers);
      if (response.statusCode == 200) {
        var result = json.decode(utf8decoder.convert(response.bodyBytes));
        success(result);
      } else {
        throw Exception('"Request failed with status: ${response.statusCode}"');
      }
    } catch (e) {
      fail(e);
    } finally {
      if (complete != null) {
        complete();
      }
    }
  }

  //网络请求封装方式二:返回Future,结合 then ==> catchError ==>whenComplete,类似JS
  static Future requestData(String url, {Map<String, String> headers}) async {
    try {
      var response = await http.get(Uri.parse(url), headers: headers);
      if (response.statusCode == 200) {
        var result = json.decode(utf8decoder.convert(response.bodyBytes));
        return result;
      } else {
        throw Exception('"Request failed with status: ${response.statusCode}"');
      }
    } catch (e) {
      Future.error(e);
    }
  }
}

()里面都是可传可不传的参数。

数据的解析。。

 Issue.fromJson(Map<String, dynamic> json) {
    total = json['total'];
    date = json['date'];
    publishTime = json['publishTime'];
    releaseTime = json['releaseTime'];
    count = json['count'];
    if (json['itemList'] != null) {
      itemList = [];
      (json['itemList'] as List).forEach((v) {
        itemList.add(new Item.fromJson(v));
      });
    }
    type = json['type'];
    nextPageUrl = json['nextPageUrl'];
  }

 Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = new Map<String, dynamic>();
    data['total'] = this.total;
    data['date'] = this.date;
    data['publishTime'] = this.publishTime;
    data['releaseTime'] = this.releaseTime;
    data['count'] = this.count;
    if (this.itemList != null) {
      data['itemList'] = this.itemList.map((v) => v.toJson()).toList();
    }
    data['type'] = this.type;
    data['nextPageUrl'] = this.nextPageUrl;
    return data;
  }

主要是map 的使用。。。

常用的变量

class DString {
  DString._();

  static const String exit_tip = '再按一次退出';
  static const String daily_paper = '日报';
  static const String discover = '发现';
  static const String hot = '热门';
  static const String mime = '我的';
}

Flutter中的数据改变监听ChangeNotifier

https://blog.csdn.net/Mr_Tony/article/details/112345491

如何创建一个model 。。根据首页中配置

image.png

utils 的使用


image.png
library lib_utils;

export 'package:lib_utils/event_bus.dart';
export 'package:lib_utils/commom_uitl.dart';
export 'package:lib_utils/date_util.dart';
export 'package:lib_utils/share_util.dart';
export 'package:lib_utils/toast_util.dart';
export 'package:lib_utils/view_util.dart';

SharedPreferences 的使用

缓存图片的使用

//封装带缓存的Image
Widget cacheImage(String url,
    {double width,
      double height,
      fit: BoxFit.cover,
      BorderRadius borderRadius,
      BoxShape shape = BoxShape.rectangle,
      bool clearMemoryCacheWhenDispose = false}) {
  return ExtendedImage.network(
    url,
    shape: shape,
    height: height,
    width: width,
    fit: fit,
    borderRadius: borderRadius,
    clearMemoryCacheWhenDispose:
    clearMemoryCacheWhenDispose, //图片从 tree 中移除,清掉内存缓存,以减少内存压力
  );
}

ImageProvider cachedNetworkImageProvider(String url) {
  return ExtendedNetworkImageProvider(url);
}

basemodel 的使用。


import 'package:flutter/material.dart';
import 'package:lib_ui/widget/loading_container.dart';

class BaseChangeNotifierModel with ChangeNotifier{

  bool _dispose = false;
  ViewState viewState = ViewState.loading;

  @override
  void dispose() {
    super.dispose();
    _dispose = true;
  }

  @override
  void notifyListeners() {
    //防止页面销毁后,网络数据请求回来后调用notifyListeners方法,_debugAssertNotDisposed()报错问题
    //另一种方案在页面销毁时取消网络请求
    if(!_dispose){
      super.notifyListeners();
    }
  }
}

lib_video 的使用
自定义VideoWidget
视频的中的MaterialControls 。。控制器,主要是手势的变化。。好多android 思想在里面。。

showModalBottomSheet 的使用,
很多细节的东西在里面。

android 部分配置。我看了一些。基本上没有变化。


image.png image.png

只使用了一个第三方。


image.png

视频详情页面
AnnotatedRegion 的使用
flutter开发使用AnnotatedRegion修改状态栏字体颜色,导致导航栏也变黑了的解决方法

@override
  Widget build(BuildContext context) {

    return AnnotatedRegion<SystemUiOverlayStyle>(
      value: SystemUiOverlayStyle.light,
      child: Material(child:Scaffold(),),);
  }

Hero 的使用 这个是动画处理。。。这个是第二个动画了。。。

CustomScrollView 的使用。

就是适配很重要了。。flutter 中很多调试好的,但在个别机子上有坏的现象,例如

image.png

还有很多,任重而道远。。。。

路随远行则将至,事虽难做则成。共勉。

上一篇下一篇

猜你喜欢

热点阅读