这些实用例子课程是从最基础的开始的,但是不是一个小组件一个小部件的教,只要装完flutter的环境的人都适合学习,开发工具用的还是vs Code,当然你们用其他的也可以。
flutter教程网送给学员的一句话: ===>课程还在继续更新~
(每天进步一点点,无形跨出一大步)
视频链接:https://www.bilibili.com/video/av46276578/ (免费,禁止倒卖)
学习方法:
你们看完教程推荐第一次遍的时候不要直接跟着敲,不然很多思路没有学到,或者没有跟上,第一遍推荐不要加速,不要跟着敲,直接完整看完一遍,和边思考,然后第二遍试着跟着敲,写完一个widget或者void就暂停然后跟着敲一遍出来,第二遍推荐1.5倍速度,
如果时间允许的话就第三遍直接不看教程直接敲出来试试,只为高效学习,不然思路不对怎么学都很容易忘记,浪费时间。
记录笔记:
大家有什么觉得比较重要的点可以用云笔记记录,下次写的时候忘记了可以直接拿出来,没有谁是能看一遍就直接完全记住而且多年不忘的,也没有谁是天生就什么都会的。
文档说明:
本人不创造知识,只做知识的学习者和搬运工,希望能帮到更多共同兴趣的爱好者,为flutter做出小小的贡献。
教程目录:
更新中.....
QQ群 or 微信群:
QQ群:874592746 (直接申请)
微信群:(秒拉进群)
视频链接:https://www.bilibili.com/video/av46276578/?p=2
这节课主要讲的是一个单屏的启动动画,其实很简单的,之前以为大家都会就没讲,然后有位小伙伴私聊我,说让我讲一下,因为很多软件用的都是单屏或者单屏下面还有跳过按钮倒计时数字啥的,这个大家随机应变应该会感觉很简单的,看完我的这些教程的朋友,
那我就不说那么多了直接开始文字教程。
main等东西就不说了,home里面写了个SingleScreen()然后我们就创建文件之后导入了,SingleScreen是一个动态的widget类,我们在里面就写个充满屏幕的图片就行了,用的图片获取方式是network,
然后我们写了个初始化,里面有个倒计时,
void initState() { super.initState(); conutDown(); }
然后倒计时里面我们写了个延时的东西,里面的参数是转到新页面的方法
void conutDown() { var _duration = Duration(seconds: 3); Future.delayed(_duration, newPage); }
之后新页面的方法写的就是给他替换路由名字为/newPage
void newPage() { Navigator.of(context).pushReplacementNamed('/NewPage'); }
之后我们的main.dart的materialApp就接收一个新的路由并写东西,就写了给他跳转到新页面
routes: <String, WidgetBuilder> { '/NewPage' : (context) => NewPage() },
然后新页面就很简单了,就是我们的想跳转到的页面了,
Scaffold( appBar: AppBar( title: Text('单屏介绍'), centerTitle: true, ), body: Center( child: Text( '新页面', style: Theme.of(context).textTheme.display2, ), ), )
大概就是介个样子啦,那我们就来把源码呈上来了:
import 'package:flutter/material.dart'; import 'single_screen.dart'; import 'new_page.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'SingleScreen', theme: ThemeData( primaryColor: Colors.blue, ), home: SingleScreen(), routes: <String, WidgetBuilder> { '/NewPage' : (context) => NewPage() }, ); } }
import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; import 'dart:async'; class SingleScreen extends StatefulWidget { @override _SingleScreenState createState() => _SingleScreenState(); } class _SingleScreenState extends State<SingleScreen> { @override Widget build(BuildContext context) { return Container( color: Colors.white, child: Image.network( 'http://img.wxcha.com/file/201606/30/1978c43117.jpg', fit: BoxFit.cover, ), ); } void initState() { super.initState(); conutDown(); } void conutDown() { var _duration = Duration(seconds: 3); Future.delayed(_duration, newPage); } void newPage() { Navigator.of(context).pushReplacementNamed('/NewPage'); } }
import 'package:flutter/material.dart'; class NewPage extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('单屏介绍'), centerTitle: true, ), body: Center( child: Text( '新页面', style: Theme.of(context).textTheme.display2, ), ), ); } }
02 AppBar下滑渐变效果制作
视频链接:https://www.bilibili.com/video/av46276578/?p=3
AppBar下滑渐变效果制作:
首先我们要有个列表,可以上下滑动的,我们这里写的是一个ListView,然后里面填满东西,让他可以上下滑动,
我们可以发现,他上面是有一些间隔padding的,因为官方写ListView的时候给里面自带了一个安全区。
那安全区是什么呢?
所谓安全区域,就是适配现在一些刘海屏之类的非常规显示屏,在flutter中除了根据上面的方法获取到状态栏高度,
给页面加对应的状态栏高度padding,还有一个专门的widget用来显示安全区域内容:SafeArea
下面是对比图:
所以我们需要来给它删掉这个所谓的padding,
那我们就来写个媒体查询来删除Padding:
MediaQuery.removePadding把我们的ListView包起来
然后写个child来放进去,
之后里面的参数是要写个context上下文,那我们给他的值也写个context,
那我们的代码就是:
return Scaffold( body: MediaQuery.removePadding( context: context, child: ListView( children: <Widget>[ Image.network( 'https://dpic.tiankong.com/r6/eb/QJ6154840181.jpg?x-oss-process=style/670ws', height: 500.0, fit: BoxFit.cover, ), Container( height: 800.0, child: ListTile( title: Text('这是标题文字'), ), ), ], ), ), );那么我们再给MediaQuery.removePadding里面再给他写个我们要删除的方向, 我们就写个removeTop他是bool型的我们就写个true,也就是顶部,他是有几个方向的, 我们想删除哪个就删除哪个,具体看需求,不过我们一般都是删除顶部的。 然后我们写个NotificationListener来监听他的状态, 里面的onNotification参数我们就用notification来接收,然后写个if判断,
onNotification: (notification) { // notification 是否为 滚动的更新通知的值 // 并且他的深度设置为0,也就是只监听最近的一个ListView, // 其他过滤掉 if(notification is ScrollUpdateNotification && notification.depth == 0) { // 如果是的话 _onScroll(notification.metrics.pixels); } },然后我们还没有_onScroll,现在就来写个_onScroll,里面传一个offset,
这个offset就是我们滚动的距离,然后我们来打印一下offset,
void _onScroll(offset) { print(offset); }然后我们用层叠组件Stack来包裹我们body里面的东西,方便展示我们的AppBar,
他这个层叠组件就像上次我讲过的一个Overlay覆盖组件一样,是写前面的就在下面,
后写的就会把前面的覆盖掉,所以我们的AppBar是写在后面的。
然后我们是需要控制他的透明度的,就写个Opacity,
Opacity有个opacity是必写的,不然报错,
我们给他写个1
然后就可以来写AppBar了,因为AppBar不怎么灵活,我们给他加个Container然后高度80
写个AppBar
Opacity( opacity: opacityValue, child: Container( height: 80, child: AppBar( title: Text('这是AppBar'), centerTitle: true, ), ), ),然后我们定义一个 double opacityValue = 0; 给opacity换上去,
再写个const maxoffset = 100;在类的外面,让他在100的时候完全变成蓝色
然后我们_onScroll来计算一下并且给他setState一下赋值:
void _onScroll(offset) { double alpha = offset/maxoffset; if(alpha < 0) { alpha = 0; } else if(alpha > 1){ alpha = 1; } setState(() { opacityValue = alpha; }); print(offset); }现在我们来呈上我们的源码文件:
import 'package:flutter/material.dart'; import 'gradient_demo.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: '渐变AppBar', theme: ThemeData( primaryColor: Colors.red, ), home: GradientDemo(), ); } }
import 'package:flutter/material.dart'; class GradientDemo extends StatefulWidget { @override _GradientDemoState createState() => _GradientDemoState(); } const maxOffset = 100; class _GradientDemoState extends State<GradientDemo> { double opacityValue = 0; void _onScrol(offset) { double alpha = offset / maxOffset; if (alpha < 0) { alpha = 0; } else if (alpha > 1) { alpha = 1; } setState(() { opacityValue = alpha; }); print(offset); } @override Widget build(BuildContext context) { return Scaffold( body: Stack( children: <Widget>[ MediaQuery.removePadding( removeTop: true, context: context, child: NotificationListener( onNotification: (notification) { // 如果notification 是他的滚动更新通知的值, // 并且深度限制为0 if (notification is ScrollUpdateNotification && notification.depth == 0) { // 如果是 _onScrol(notification.metrics.pixels); } }, child: ListView( children: <Widget>[ Image.network( 'https://dpic.tiankong.com/r6/eb/QJ6154840181.jpg?x-oss-process=style/670ws', height: 180, fit: BoxFit.cover, ), Container( height: 800, child: Text( '渐变AppBar', style: Theme.of(context).textTheme.display2, ), ), ], ), ), ), Opacity( opacity: opacityValue, child: Container( height: 80, child: AppBar( title: Text('渐变ApBar'), centerTitle: true, ), ), ), ], )); } }
发表评论