用Flutter写一个毕设app的经验(flutter 设计稿)
Flutter我也不是很了解,但自从我接触了这门跨端技术开发之后,我是深深地喜欢上了它的UI、它的交互和动画。很多人会吐槽它的无限嵌套,这是它有所不足的地方。喜欢一项技术,就像喜欢一个人一样,看到Ta的优点的同时也能包容它的缺点。
我认识一位才女,她在今年的毕业设计中,设计了一款app的UI。我当时有个想法,把她的毕设开发出一个app。后面联系的机会也比较少,各自忙各自的去啦!直到2021.08.23号,我跟她拿了设计稿,并通过新技术开始了一个周的开发经历。
看到设计稿,我第一感觉是:在设计和配色方面让人联想到文化和古风
应用介绍:运用扁平风插画的形式来表现苗族特有的民族特征,文化和乐器,以最新潮的方式推广给每一位用户,让《苗韵》成为一款趣味性的科普APP。
应用开发后的交互视频
经验分享:
1.实现引导页
引导页面
两种选择,可以选择pageview自行搭建自己的引导页;
stack(children:[ Positioned.fill( // 根据需求构建自己的应到也 child: PageView.builder() ) ] // 指示器内容 buildDot())
另外一种选择是可以选择使用插件:这个插件构建出来的引导页也不错
intro_views_flutter
2.实现组件突出效果
像这种卡片上移的效果,对于flutter来说不太好实现,而且实现之后还需要注意层级会遮挡。
两种实现方式:
使用stack布局,有一个缺点,如果内容是长列表,那会导致里面的内容无法正常的滑动,比较适合小卡片布局。比如上图中向外突出的乐器。
stack(children:[ position( top:-30, //关键代码在这里 ), ])
使用 配合transform: Matrix4.translationValues(0.0, -100.0, 0.0),配合ClipPath
ClipPath( //定义裁切路径 clipper: BackgroundClipper(), child: Container( ), )
BackgroundClipper 用于裁剪上移后,容器下面部分的内容。
4.实现全局加载动画和单个加载动画
插件推荐:
# loading动画 flutter_spinkit: ^5.0.0
全局加载模态
import 'package:flutter/material.dart';import 'package:flutter_miaoyun/utills/color_utils.dart';import 'package:flutter_spinkit/flutter_spinkit.dart';class Loading extends StatelessWidget { static void show(BuildContext context) { showDialog( barrierDismissible: true, context: context, builder: (ctx) => Theme( data: Theme.of(ctx).copyWith(dialogBackgroundColor: Colors.transparent), child: Loading(), ), ); } static void dismiss(context) { Navigator.pop(context); } @override Widget build(BuildContext context) { return Container( color: Colors.transparent, child: Center( child: Container( decoration: BoxDecoration( color: Colors.transparent, borderRadius: BorderRadius.circular(5), ), width: 60, height: 60, alignment: Alignment.center, child: Column( mainAxisSize: MainAxisSize.min, children: <Widget>[ SpinKitCubeGrid( color: gColor("#C8AD6B"), size: 46.0, ) ], ), ), ), ); }}// 调用 Loading.show() 隐藏 Loading.dismiss()
单个加载动画,可参考插件的demo 自行选用
5.实现点击菜单移动到对应的内容区域
插件:
# 跳转到对应的页面 scroll_to_index: ^2.0.0
6.音乐播放的实现
在使用这个插件的时候,我在编译的时候遇到了kotlin版本不匹配问题,我的解决方案是看插件的example使用的是哪个版本,然后再Android Stadio 中更新kotlin到对应的版本。之后就完美解决。
# 音频播放 audioplayers: ^0.19.1
音频播放的逻辑。大概的分享一下。我是将audio的实例对象初始化后存到controller中,这样就可以在多个页面对音乐进行控制。
7.制作闪屏页
插件推荐
flutter_native_splash配置dev_dependencies: flutter_test: sdk: flutter flutter_native_splash: ^1.2.0flutter_native_splash: # color: "#42a5f5" #image: assets/splash/icon.png background_image: assets/guides/splash.png # color_dark: "#042a49" #image_dark: assets/splash/icon.png background_image_dark: assets/guides/splash.png android: true ios: true web: true fullscreen: true使用:三步走flutter clean flutter pub getflutter pub run flutter_native_splash:create
8.本地存储
插件推荐
shared_preferences
封装
import 'dart:convert';import 'package:shared_preferences/shared_preferences.dart';class SPUtils { // 静态实例 static SharedPreferences _sharedPreferences; // 应用启动时需要调用 // 初始化 static Future<bool> init() async { _sharedPreferences = await SharedPreferences.getInstance(); return true; } // 清除数据 static void remove(String key) async { if (_sharedPreferences.containsKey(key)) { _sharedPreferences.remove(key); } } // 异步保存数据类型 static Future save(String key, dynamic value) async { if (value is String) { _sharedPreferences.setString(key, value); } else if (value is bool) { _sharedPreferences.setBool(key, value); } else if (value is double) { _sharedPreferences.setDouble(key, value); } else if (value is int) { _sharedPreferences.setInt(key, value); } else if (value is List<String>) { _sharedPreferences.setStringList(key, value); } } // 异步读取数据 static Future<String> getString(String key) async { return _sharedPreferences.getString(key); } static Future<int> getInt(String key) async { return _sharedPreferences.getInt(key); } static Future<bool> getBool(String key) async { return _sharedPreferences.getBool(key); } static Future<double> getDouble(String key) async { return _sharedPreferences.getDouble(key); } // 保存自定义对象 static Future saveObject(String key, dynamic value) async { // 通过json 将Object对象编译成String类型保存 _sharedPreferences.setString(key, json.encode(value)); } // 获取自定义对象 static dynamic getObject(String key) { String _data = _sharedPreferences.getString(key); return (_data == null || _data.isEmpty) ? null : json.decode(_data); } // 保存类表数据 static Future<bool> putObjectList(String key, List<Object> list) { List<String> _dataList = list?.map((value) { return json.encode(value); })?.toList(); return _sharedPreferences.setStringList(key, _dataList); } // 获取对象集合数据 // 返回的是List <Map<String ,dynamic>>类型 static List<Map> getObjectList(String key) { if (_sharedPreferences == null) return null; List<String> dataList = _sharedPreferences.getStringList(key); return dataList?.map((value) { Map _dataMap = json.decode(value); return _dataMap; })?.toList(); }}
以上是在开发过程中觉得不错,可以分享给flutter爱好者的相关知识。也是我在开发过程中遇到的一些技术难点。淡然有很多。要开发一个app,遇到的问题绝不是上面的这一点点。大家有不懂得多多交流。