听诗吧-APP端分享总结

565 阅读3分钟

简介

听诗吧 是一个集 诗词/诗人推荐、搜索、简介、赏析、朗读(下个版本) 于一体的诗词兴趣 APP。涵盖了从古代到近代的 20,000 位诗人的 500,000 首诗词作品,叙事诗、抒情诗、送别诗、边塞诗、山水田园诗、怀古诗、悼亡诗,咏物诗 应有尽有😁。

  • APP 端基于 Google 最新研发的 Flutter UI 框架,一套代码库高效构建多平台精美应用(移动、Web、桌面和嵌入式平台)😊,配合 MaterialDesign 的设计风格 和 卡片式布局 让你眼前一亮。更有微信分享功能,好东西当然要分享👍~

项目地址: github.com/mmtou/liste…
下载体验: github.com/mmtou/liste…

  • API 端基于 SpringBoot 微服务架构 和 轻量级的 MySQL 数据库,给你带来高效、稳定的服务体验。更集成了百度的语音合成技术,让你畅快的享受诗词带来的乐趣😍。

先睹为快

app端项目结构

├── components // 组件
│   ├── avatar.dart // 头像组件,前期根据名称生成对应颜色的头像
│   ├── empty.dart // 置空组件
│   ├── poet.dart // 诗人列表
│   ├── poet_item.dart // 单个诗人
│   ├── poetry.dart // 诗词列表
│   ├── poetry_item.dart // 单个诗词
│   └── recommend.dart // 推荐页组件
├── main.dart
├── utils // 工具类
│   ├── common.dart // 通用工具
│   ├── constant.dart // 常量
│   ├── favorite.dart // 点爱心的管理
│   ├── http.dart // 网络请求
└── views // 页面
    ├── feedback.dart // 反馈
    ├── index.dart // 首页
    ├── poet_detail.dart // 诗人
    ├── poetry_detail.dart // 诗词
    └── search.dart // 搜索

核心代码

  1. APP 启动时从本地读取点赞列表
void main() {
  if (Platform.isAndroid) {
    SystemUiOverlayStyle systemUiOverlayStyle =
        SystemUiOverlayStyle(statusBarColor: Colors.transparent);
    SystemChrome.setSystemUIOverlayStyle(systemUiOverlayStyle);
  }

  WidgetsFlutterBinding.ensureInitialized();
  run();
}

void run() async {
  await Favorite.getInstance();
  runApp(MyApp());
}
...

class Favorite {
  ...

  static Future<bool> getInstance() async {
    prefs = await SharedPreferences.getInstance();
    favorites = prefs.getStringList('favorites') ?? [];
    return true;
  }
  
  ...
}
  1. 头像组件,根据诗人名称,计算16位UTF-16代码单元,然后根据规定的颜色数组长度取余,得到index,从颜色数组的中得到某个色值,即为头像的背景色。
Widget build(BuildContext context) {
    String name = Common.getShortName(authorName);
    int index = name.codeUnitAt(0) % Constant.avatarColors.length;
    Color color = Constant.avatarColors[index];

    return InkWell(
      onTap: () {
        if (authorId != null) {
          Common.toPoetDetail(context, authorId);
        }
      },
      child: CircleAvatar(
        backgroundColor: color,
        child: Center(
          child: Text(
            name,
            style: TextStyle(
              fontWeight: FontWeight.w600,
              color: Colors.white,
            ),
          ),
        ),
      ),
    );
  }
  1. 微信分享,集成 fluwx 第三方插件,快速实现微信分享、登录、支付等功能。
    1. 创建 keystore,修改相应配置
    2. 查看签名,执行keytool -list -v -keystore key.keystore命令后的MD5去掉:转为小写即为微信开放平台的签名。
    3. 登录微信开放平台创建应用,填写包名、签名等信息
    fluwx.registerWxApi(
        appId: "xxxx",
        universalLink: "xxxx");
    fluwx.shareToWeChat(WeChatShareTextModel(
        text: content,
        transaction: "transaction}",
        scene: WeChatScene.SESSION));
    

请移步 flutterchina.club/android-rel… 查看签名配置

  1. http.dart单例模式,减少资源浪费
import 'package:bot_toast/bot_toast.dart';
import 'package:dio/dio.dart';

import './constant.dart';

class Http {
  Dio _dio;

  // 工厂模式
  factory Http() => _getInstance();

  static Http get instance => _getInstance();
  static Http _instance;

  Http._internal() {
    // 初始化

    if (_dio == null) {
      _dio = Dio(
        BaseOptions(
            baseUrl: Constant.host,
            connectTimeout: 10000,
            receiveTimeout: 6000,
            headers: {'Content-Type': 'application/json'}),
      );

      _dio.interceptors
          .add(InterceptorsWrapper(onRequest: (RequestOptions options) async {
        BotToast.showLoading();
        return options; //continue
      }, onResponse: (Response response) async {
        BotToast.closeAllLoading();
        var data = response.data;
        if (!data['success']) {
          BotToast.showText(text: data['message']);
          throw (data['message']);
        }
        return response.data['result']; // continue
      }, onError: (DioError e) async {
        BotToast.closeAllLoading();
        if ('DioErrorType.DEFAULT' == e.type.toString()) {
          BotToast.showText(text: e.error);
        } else {
          BotToast.showText(text: '服务器异常');
        }
        // Do something with response error
        return e; //continue
      }));
    }
  }

  static Http _getInstance() {
    if (_instance == null) {
      _instance = Http._internal();
    }
    return _instance;
  }

  Future get(uri, {queryParameters}) async {
    try {
      Response response = await _dio.get(uri, queryParameters: queryParameters);
      print(response);
      return response.data;
    } catch (e) {
      print(e);
    }
  }

  Future post(uri, {json}) async {
    try {
      Response response = await _dio.post(uri, data: json);
      return response.data;
    } catch (e) {
      print(e);
    }
  }
}
  1. 使用TabView时,避免页面重绘,造成资源浪费和体验下降;子组件集成AutomaticKeepAliveClientMixin实现缓存策略,且在build中加入super.build(context);
class Poetry extends StatefulWidget {
  @override
  _PoetryState createState() => _PoetryState();
}

class _PoetryState extends State<Poetry> with AutomaticKeepAliveClientMixin {
  List list;
  int pageNum = 1;

  @override
  Widget build(BuildContext context) {
    super.build(context);
    return EasyRefresh(
      header:
          BezierCircleHeader(backgroundColor: Theme.of(context).primaryColor),
      footer:
          BezierBounceFooter(backgroundColor: Theme.of(context).primaryColor),
      onRefresh: () => refresh(),
      onLoad: () => onLoad(),
      child: this.list == null
          ? Empty('暂无数据~')
          : ListView.separated(
              padding: EdgeInsets.all(16),
              itemCount: this.list.length,
              separatorBuilder: (BuildContext context, int index) => Container(
                    height: 12,
                  ),
              itemBuilder: (BuildContext context, int index) {
                var item = this.list[index];
                return PoetryItem(item);
              }),
    );
  }

  refresh() async {
    this.pageNum = 1;
    this.list = [];
    await this.getList();
    return true;
  }

  onLoad() async {
    pageNum += 1;
    await this.getList();
    return true;
  }

  // 诗词列表
  getList() async {
    var data = await Http().get('/poetry/list?pageNum=$pageNum');
    if (data != null) {
      List list = data['list'];
      this.list = this.list ?? [];
      setState(() {
        this.list.addAll(list);
      });
    }
  }

  @override
  void initState() {
    super.initState();
    this.getList();
  }

  @override
  bool get wantKeepAlive => true;
  1. InkWell组件可以实现点击后的波纹反馈效果,但是InkWell的child不能设置背景色,不然会覆盖掉波纹的效果。需要你又需要背景色怎么办呢?可以在InkWell上包一层Ink
Ink(
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.circular(8),
      ),
      child: InkWell(
        onTap: () => Common.toPoetryDetail(context, widget.poetry),
        child: Container(
          padding: EdgeInsets.all(12),
          decoration: BoxDecoration(
            borderRadius: BorderRadius.circular(8),
          ),
        ),
      )
    );

功能

  • 推荐
  • 广场
  • 诗人
  • 诗词详情
  • 诗人详情
  • 微信分享
  • 搜索
  • 诗词朗读
  • 登录

最后

未完待续~

愿你走出半生,归来仍是少年。