Flutter项目实操---发现
发现:
列表显示:
继续接着上一次https://www.cnblogs.com/webor2006/p/13098765.html的功能往下进行编写,接下来则来写发现这个页面的功能,先来看一下最终它长啥样?
先来显示列表,关于列表的显示在我的界面中已经使用过了,这里再来快速的温故一下,目前发现只是占了一个位:
import 'package:flutter/material.dart'; class DiscoveryPage extends StatefulWidget { @override _DiscoveryPageState createState() => _DiscoveryPageState(); } class _DiscoveryPageState extends State<DiscoveryPage> { @override Widget build(BuildContext context) { return Center( child: Text('发现'), ); } }
先定义个数组,代表列表选项:
import 'package:flutter/material.dart'; class DiscoveryPage extends StatefulWidget { @override _DiscoveryPageState createState() => _DiscoveryPageState(); } class _DiscoveryPageState extends State<DiscoveryPage> { List<Map<String, IconData>> blocks = [ { '开源众包': Icons.pageview, '开源软件': Icons.speaker_notes_off, '码云推荐': Icons.screen_share, '代码片段': Icons.assignment, }, { '扫一扫': Icons.camera_alt, '摇一摇': Icons.camera, }, { '码云封面人物': Icons.person, '线下活动': Icons.android, } ]; @override Widget build(BuildContext context) { return Center( child: Text('发现'), ); } }
为啥这次定义的数据分开了呢?
这是因为本身这次的列表界面就有分组存在,看一下界面效果图就知道了:
接下来继续, 先来显示一个轮廓:
运行:
接下来则每个块中得填充列表项,也就是ListView中再次嵌ListView,这种用法咱们还是第一次用,来瞅一下:
import 'package:flutter/material.dart'; class DiscoveryPage extends StatefulWidget { @override _DiscoveryPageState createState() => _DiscoveryPageState(); } class _DiscoveryPageState extends State<DiscoveryPage> { List<Map<String, IconData>> blocks = [ { '开源众包': Icons.pageview, '开源软件': Icons.speaker_notes_off, '码云推荐': Icons.screen_share, '代码片段': Icons.assignment, }, { '扫一扫': Icons.camera_alt, '摇一摇': Icons.camera, }, { '码云封面人物': Icons.person, '线下活动': Icons.android, } ]; @override Widget build(BuildContext context) { return ListView.builder( itemCount: blocks.length, itemBuilder: (context, index) { return Container( height: 200.0, decoration: BoxDecoration( border: Border( top: BorderSide( width: 1.0, color: Color(0xffaaaaaa), ), bottom: BorderSide( width: 1.0, color: Color(0xffaaaaaa), ), ), ), child: ListView.separated( itemBuilder: (context, itemIndex) { return InkWell( onTap: () {}, child: Container( child: ListTile( leading: Icon(blocks[index].values.elementAt(itemIndex)), title: Text(blocks[index].keys.elementAt(itemIndex)), trailing: Icon(Icons.arrow_forward_ios), ), ), ); }, separatorBuilder: (context, itemIndex) { return Divider(); }, itemCount: blocks[index].length), ); }, ); } }
运行看一下:
不过布局看着有点别扭,原因是咱们这个高度定死了:
发现内容出不来了:
这是咋回事呢?由于咱们目前的场景是ListView里面又套了一个ListView,这里需要增加一个属性,如下:
此时内容就正常了:
接下来其实目前是有个问题的,目前由于用的手机分辨率比较高还看不出来,咱们换一台较低分辨率的演示一下就知道了:
看到木有,滑不动了,其实是由于滑动事件对第一层的ListView给消费掉了,对于ListView里面再嵌ListView是会有滑动冲突的问题,这也是问题之所在,而在Flutter中解决滑动冲突非常之简单,加个属性就解决了,而在Android中想想是多么麻烦,具体如下:
然后咱们再将块与块之间增加一点间距,好看一点,那如何增加呢?看修改:
运行看一下效果:
其中有个小细节提一下,在之前已经有提过:
其实就是android material风格,看一下:
就是这种效果。
处理点击事件:
开源众包:
这里先来处理这块的点击:
这里只以“开源众包”点击事件为例实现一下:
然后找一个开源众包的URL,直接上开源中国的官网找一下链接:
"https://zb.oschina.net/",是它,所以咱们写一下跳转,在之前的登录中已经用到了,直接贴代码:
然后新建一个WebView的页面:
import 'package:flutter/material.dart'; class CommonWebPage extends StatefulWidget { final String title; final String url; CommonWebPage({Key key, this.title, this.url}) : assert(title != null), assert(url != null), super(key: key); @override _CommonWebPageState createState() => _CommonWebPageState(); } class _CommonWebPageState extends State<CommonWebPage> { @override Widget build(BuildContext context) { return Container(); } }
接下来则来编写这个WebView页面,之前已经使用过一次了,这里再来温故一下:
import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_osc_client/constants/constants.dart'; import 'package:flutter_webview_plugin/flutter_webview_plugin.dart'; class CommonWebPage extends StatefulWidget { final String title; final String url; CommonWebPage({Key key, this.title, this.url}) : assert(title != null), assert(url != null), super(key: key); @override _CommonWebPageState createState() => _CommonWebPageState(); } class _CommonWebPageState extends State<CommonWebPage> { bool isLoading = true; @override Widget build(BuildContext context) { List<Widget> _appBarTitle = [ Text( widget.title, style: TextStyle( color: Color(AppColors.APPBAR), ), ), ]; if (isLoading) { _appBarTitle.add(SizedBox( width: 10.0, )); _appBarTitle.add(CupertinoActivityIndicator()); } return WebviewScaffold( url: widget.url, appBar: AppBar( title: Row( children: _appBarTitle, ), iconTheme: IconThemeData(color: Color(AppColors.APPBAR)), //0412 added ), withJavascript: true, //允许执行js withLocalStorage: true, //允许本地存储 withZoom: true, //允许网页缩放 ); } }
接下来增加WebView的监听:
运行一下:
扫一扫:
接下来再来处理扫一扫的点击事件:
对于扫一扫肯定得用三方开源的,所以用这个库来做:
看一下怎么用的:
然后看下拉取下来的版本号:
所以:
那如何调用呢?
所以咱们来整一下:
运行看一下:
报错了。。上面错误提示说是需要改一下最小支持的sdk为16以上,其实改了之后还是报,这里换一个稍低的版本:
然后再编译,发现还是出错:
androidx,也就是咱们目前还是用的support,那具体怎么解决这个错呢,得按如下步骤改一下:
1、修改 android/gradle/wrapper/gradle-wrapper.properties 为gradle-4.10.2-all.zip及以上:
2、修改 android/build.gradle :
groovy dependencies { classpath 'com.android.tools.build:gradle:3.2.1' } 改为 groovy dependencies { classpath 'com.android.tools.build:gradle:3.3.0' }
改为:
3、修改 android/gradle.properties,加上下面两句 :
android.enableJetifier=true android.useAndroidX=true
4、修改 android/app/build.gradle :
首先,确保 compileSdkVersion 和 targetSdkVersion 至少为 28 :
android{ ... compileSdkVersion 28 ... defaultConfig{ ... targetSdkVersion 28 ... } ... }
5、将以下的support都改为androidx:
一切修改就绪,接下来运行扫一扫,发现还是报错了:
哦,难道是忘了加相机权限了,其实不是的,这是需要在项目工程下执行“flutter clean”既可以解决这个错,如下:
然后再次运行就好了:
咱们这里找一个二维码扫一下能出结果就成:
摇一摇:
添加点击事件:
这个就稍复杂一些,先添加一个点击事件跳到一个页面:
import 'package:flutter/material.dart'; import 'package:flutter_osc_client/constants/constants.dart'; class ShakePage extends StatefulWidget { @override _ShakePageState createState() => _ShakePageState(); } class _ShakePageState extends State<ShakePage> { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( elevation: 0.0, title: Text( '摇一摇', style: TextStyle(color: Color(AppColors.APPBAR)), ), iconTheme: IconThemeData(color: Color(AppColors.APPBAR)), ), body: Center( child: Text('摇一摇'), ), ); } }
页面搭建:
先来看一下它长啥样:
所以咱们来搭建一下,由于这样的界面咱们在之前已经用过了,所以这里就不过多的解释说明了:
import 'package:flutter/material.dart'; import 'package:flutter_osc_client/constants/constants.dart'; class ShakePage extends StatefulWidget { @override _ShakePageState createState() => _ShakePageState(); } class _ShakePageState extends State<ShakePage> { bool isShaked = false; int _curentIndex = 0; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( elevation: 0.0, title: Text( '摇一摇', style: TextStyle(color: Color(AppColors.APPBAR)), ), iconTheme: IconThemeData(color: Color(AppColors.APPBAR)), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ Image.asset( 'assets/images/shake.png', width: 120.0, height: 120.0, ), SizedBox( height: 10.0, ), Text('摇一摇获取礼品'), ], ), ), bottomNavigationBar: BottomNavigationBar( items: [ BottomNavigationBarItem(icon: Icon(Icons.folder), title: Text('礼品')), BottomNavigationBarItem( icon: Icon(Icons.assignment), title: Text('资讯')) ], currentIndex: _curentIndex, onTap: (index) { if (!mounted) return; setState(() { _curentIndex = index; }); }, ), ); } }
其中用到了一个新图片:
在yaml文件中声明一下:
运行:
开始实现:
1、集成三方依赖库:
这里对于摇一摇其实没有太成熟好用的库,不过也能搜到一些,比如:
但是这里采用自己来编写逻辑,只集成传感器的功能库,如下:
而它的使用看一下:
所以咱们来将它集成进来:
2、增加加速度感应监听:
3、摇一摇逻辑实现:
接下来则实现摇一摇的具体处理,不过先来给它加一个震动,此时又需要用到三方的库了:
import 'dart:async'; import 'dart:math'; import 'package:flutter/material.dart'; import 'package:flutter_osc_client/constants/constants.dart'; import 'package:sensors/sensors.dart'; import 'package:vibration/vibration.dart'; class ShakePage extends StatefulWidget { @override _ShakePageState createState() => _ShakePageState(); } class _ShakePageState extends State<ShakePage> { bool isShaked = false; int _curentIndex = 0; StreamSubscription _streamSubscription; static const int SHAKE_TIMEOUT = 500; static const double SHAKE_THRESHOLD = 3.25; var _lastTime = 0; @override void initState() { super.initState(); _streamSubscription = accelerometerEvents.listen((AccelerometerEvent event) { var now = DateTime.now().millisecondsSinceEpoch; if ((now - _lastTime) > SHAKE_TIMEOUT) { //获得加速度的x,y,z值 var x = event.x; var y = event.y; var z = event.z; double acce = sqrt(x * x + y * y + z * z) - 9.8; //9.8是g,加速度公式,貌似是初中的物理,反正我是忘了 if (acce > SHAKE_THRESHOLD) { print('摇一摇'); //手机晃动了 Vibration.vibrate(); _lastTime = now; if (!mounted) return; setState(() { isShaked = true; }); } } }); } @override void dispose() { super.dispose(); _streamSubscription.cancel(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( elevation: 0.0, title: Text( '摇一摇', style: TextStyle(color: Color(AppColors.APPBAR)), ), iconTheme: IconThemeData(color: Color(AppColors.APPBAR)), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ Image.asset( 'assets/images/shake.png', width: 120.0, height: 120.0, ), SizedBox( height: 10.0, ), Text(isShaked ? '活动已结束!' : '摇一摇获取礼品'), ], ), ), bottomNavigationBar: BottomNavigationBar( items: [ BottomNavigationBarItem(icon: Icon(Icons.folder), title: Text('礼品')), BottomNavigationBarItem( icon: Icon(Icons.assignment), title: Text('资讯')) ], currentIndex: _curentIndex, onTap: (index) { if (!mounted) return; setState(() { _curentIndex = index; isShaked = false; }); }, ), ); } }
下面咱们运行试一下:
图片中感知不到震动与摇动,具体自行体验下既可,当然摇一摇应该得去请求相应的接口的,这里由于木有相关接口就暂且忽略了。