9.4 NotificationListener

  • NotificationListener是一个Widget,需要将ListView添加到NotificationListener中

class MyListenerApp extends StatelessWidget {

@override

Widget build(BuildContext context) {

return MaterialApp(

home: Scaffold(

body: NotificationListener(

//添加NotificationListener作为父容器

//注册通知回调

onNotification: (scrollNotification) {

//开始滑动

if (scrollNotification is ScrollStartNotification) {

//scrollNotification.metrics.pixels 滑动的位置

print(‘scroll start ${scrollNotification.metrics.pixels}’);

} else if (scrollNotification is ScrollUpdateNotification) {

//滑动中

print(‘scroll update’);

} else if (scrollNotification is ScrollEndNotification) {

//滑动结束

print(‘scroll end’);

}

return null;

},

child: ListView.builder(

itemCount: 100,

itemExtent: 70,

itemBuilder: (context, index) => ListTile(

title: Text(‘index $index’),

)),

),

),

);

}

}

10. 布局容器


10.1 Container,Padding,Center

  • Container内部提供了间距,背景样式,圆角边框等基础属性,可以控制子Widget的摆放方式(居中,左,右)

  • Padding 设置间距,将Widget放里面

  • Center 设置居中,将Widget放里面

getContainer() {

return Container(

child: Center(

child: Text(‘Container(容器)在UI框架中是一个很常见的概念,Flutter也不例外。’),

),

//内边距

padding: EdgeInsets.all(18.0),

//外边距

margin: EdgeInsets.all(44.0),

width: 180.0,

height: 240,

//子Widget居中对齐

/* alignment: Alignment.center,*/

//Container样式

decoration: BoxDecoration(

//背景色

color: Colors.red,

//圆角边框

borderRadius: BorderRadius.circular(10.0),

),

);

}

getPadding() {

//只需要设置边距 可以使用Padding

return Padding(

padding: EdgeInsets.all(44.0),

child: Text(‘我是Padding’),

);

}

getCenter() {

//直接居中

return Center(

child: Text(‘center text’),

);

}

10.2 Row,Column,Expanded

  • Row是水平布局

  • Column是垂直布局

  • Expanded表示将剩余的空间,如何分配

  • Row 与 Column 自身的大小由父widget的大小、子widget的大小、以及mainSize设置共同决定(mainAxisSize和crossAxisSize)

  • 主轴(纵轴)值为max:主轴(纵轴)大小等于屏幕主轴(纵轴)方向大小或者父widget主轴(纵轴)方向大小

  • 主轴(纵轴)值为min: 所有子widget组合在一起的主轴(纵轴)大小

//Row的用法示范

Row(

children: [

Container(color: Colors.yellow, width: 60, height: 80,),

Container(color: Colors.red, width: 100, height: 180,),

Container(color: Colors.black, width: 60, height: 80,),

Container(color: Colors.green, width: 60, height: 80,),

],

);

//Column的用法示范

Column(

children: [

Container(color: Colors.yellow, width: 60, height: 80,),

Container(color: Colors.red, width: 100, height: 180,),

Container(color: Colors.black, width: 60, height: 80,),

Container(color: Colors.green, width: 60, height: 80,),

],

);

//第一个和最后一个平分

Row(

children: [

Expanded(flex: 1, child: Container(color: Colors.yellow, height: 60)), //设置了flex=1,因此宽度由Expanded来分配

Container(color: Colors.red, width: 100, height: 180,),

Container(color: Colors.black, width: 60, height: 80,),

Expanded(flex: 1, child: Container(color: Colors.green,height: 60),)/设置了flex=1,因此宽度由Expanded来分配

],

);

对齐方式

  • 根据主轴与纵轴,设置子Widget在这两个方向上的对齐规则mainAxisAlignment与crossAxisAlignment.比如主轴方向start表示靠左对齐,center表示横向居中对齐,end表示靠右对齐,spaceEvenly表示按固定间距对齐;而纵轴方向start则表示靠上对齐,center表示纵向居中对齐,end表示靠下对齐.

控制大小

  • 如果想让容器与子Widget在主轴上完全匹配,需要通过设置Row的mainAxisSize参数为MainAxisSize.min,由所有子Widget来决定主轴方向的容器长度,即主轴方向的长度尽可能小.类似wrap_content. mainAxisSize: MainAxisSize.min, //让容器宽度与所有子Widget的宽度一致

10.3 Stack,Positioned

  • Stack,类似FrameLayout.

  • Stack提供了层叠布局的容器,而Positioned则提供了设置子Widget位置的能力.

Stack(

children: [

Container(color: Colors.yellow, width: 300, height: 300),//黄色容器

Positioned(

left: 18.0,

top: 18.0,

child: Container(color: Colors.green, width: 50, height: 50),//叠加在黄色容器之上的绿色控件

),

Positioned(

left: 18.0,

top:70.0,

child: Text(“Stack提供了层叠布局的容器”),//叠加在黄色容器之上的文本

)

],

)

  • Positioned只能在Stack中使用.

11. 自定义控件


11.1 组合控件

  • 将多个控件组合在一起

11.2 自定义控件

  • CustomPaint是用来承接自绘控件的容器,并不负责真正的绘制.

  • 画布是canvas,画笔是Paint.

  • 画成什么样子由CustomPainter来控制,将CustomPainter设置给容器CustomPaint的painter属性,我们就完成了一个自绘组件的封装

  • Paint,其实和Android中的差不多,可以配置它的各种属性,比如颜色、样式、粗细等;而画布 Canvas,则提供了各种常见的绘制方法,比如画线 drawLine、画矩形 drawRect、画点 DrawPoint、画路径 drawPath、画圆 drawCircle、画圆弧 drawArc 等。

class WheelPainter extends CustomPainter {

Paint getColoredPaint(Color color) {

Paint paint = Paint();

paint.color = color;

return paint;

}

@override

void paint(Canvas canvas, Size size) {

//半径

double wheelSize = min(size.width, size.height) / 2;

//分成6份

double nbElem = 6;

//角度

double radius = (2 * pi) / nbElem;

//包裹饼图的矩形框 center:相对于原点的偏移量

Rect boundingRect = Rect.fromCircle(

center: Offset(wheelSize, wheelSize), radius: wheelSize);

//每次画1/6圆

canvas.drawArc(

boundingRect, 0, radius, true, getColoredPaint(Colors.orange));

canvas.drawArc(

boundingRect, radius, radius, true, getColoredPaint(Colors.green));

canvas.drawArc(

boundingRect, radius * 2, radius, true, getColoredPaint(Colors.red));

canvas.drawArc(

boundingRect, radius * 3, radius, true, getColoredPaint(Colors.blue));

canvas.drawArc(

boundingRect, radius * 4, radius, true, getColoredPaint(Colors.pink));

canvas.drawArc(boundingRect, radius * 5, radius, true,

getColoredPaint(Colors.deepOrange));

}

@override

bool shouldRepaint(CustomPainter oldDelegate) {

//判断是否需要重绘,简单做下比较

return oldDelegate != this;

}

}

class Cake extends StatelessWidget {

@override

Widget build(BuildContext context) {

//CustomPaint是用来承载自定义View的容器,需要自定义一个画笔,得继承自CustomPainter

return CustomPaint(

size: Size(200, 200),

painter: WheelPainter(),

);

}

}

12. 主题定制


  • 视觉效果是易变的,我们将这些变化的部分抽离出来,把提供不同视觉效果的资源和配置按照主题进行归类,整合到一个统一的中间层去管理,这样我们就能实现主题的管理和切换.

  • Flutter中由ThemeData来统一管理主题的配置信息

  • ThemeData中涵盖了Material Design规范的可自定义部分样式,比如应用明暗模式 brightness、应用主色调 primaryColor、应用次级色调 accentColor、文本字体 fontFamily、输入框光标颜色 cursorColor 等。

  • 全局统一的视觉风格:

MaterialApp(

title: ‘Flutter Demo’,//标题

theme: ThemeData(//设置主题

brightness: Brightness.dark,//设置明暗模式为暗色

accentColor: Colors.black,//(按钮)Widget前景色为黑色

primaryColor: Colors.cyan,//主色调为青色

iconTheme:IconThemeData(color: Colors.yellow),//设置icon主题色为黄色

textTheme: TextTheme(body1: TextStyle(color: Colors.red))//设置文本颜色为红色

),

home: MyHomePage(title: ‘Flutter Demo Home Page’),

);

  • 局部主题: 需要使用Theme来对App的主题进行局部覆盖,Theme是一个单子Widget容器,将控件放里面就可以控制主题了.

  • 局部新建主题: 如果不想继承任何App全局的颜色或字体样式,可以直接新建一个ThemeData实例,依次设置对应的样式.

// 新建主题

Theme(

data: ThemeData(iconTheme: IconThemeData(color: Colors.red)),

child: Icon(Icons.favorite)

);

  • 继承主题: 如果不想在局部重写所有的样式,则可以继承App的主题,使用copyWith方法,只更新部分样式

// 继承主题

Theme(

data: Theme.of(context).copyWith(iconTheme: IconThemeData(color: Colors.green)),

child: Icon(Icons.feedback)

);

  • 主题另一个用途是样式复用.

Container(

color: Theme.of(context).primaryColor,//容器背景色复用应用主题色

child: Text(

‘Text with a background color’,

style: Theme.of(context).textTheme.title,//Text组件文本样式复用应用文本样式

));

13. 依赖管理


  • 可以把资源放任意目录,只需要使用根目录下的pubspec.yaml文件,对这些资源的所在位置进行显示声明就行.

13.1 图片

flutter:

assets:

  • assets/background.jpg #挨个指定资源路径

  • assets/loading.gif #挨个指定资源路径

  • assets/result.json #挨个指定资源路径

  • assets/icons/ #子目录批量指定

  • assets/ #根目录也是可以批量指定的

  • Flutter遵循了基于像素密度的管理方式,如1.0x,2.0x,3.0x.Flutter会根据当前设备分辨率加载最接近设备像素比例的图片资源

  • 想让Flutter适配不同的分辨率,只需要将其他分辨率的图片放到对应的分辨率子目录中.

目录如下

assets

├── background.jpg //1.0x图

├── 2.0x

│ └── background.jpg //2.0x图

└── 3.0x

在pubspec.yaml文件声明:

flutter:

assets:

  • assets/background.jpg #1.0x图资源

13.2 字体

fonts:

  • family: RobotoCondensed #字体名字

fonts:

  • asset: assets/fonts/RobotoCondensed-Regular.ttf #普通字体

  • asset: assets/fonts/RobotoCondensed-Italic.ttf

style: italic #斜体

  • asset: assets/fonts/RobotoCondensed-Bold.ttf

weight: 700 #粗体

13.3 三方库 三方组件库

  • Dart提供包管理工具: Pub,管理代码和资源

  • 对于包,通常是指定版本区间,而很少直接指定特定版本.

  • 多人协作时,建议将Dart和Flutter的SDK环境写死,统一团队的开发环境.避免因为跨SDK版本出现的API差异而导致工程问题.

dependencies:

//1. #路径依赖

package1:

path: …/package1/

//2. github

date_format:

git:

url: https://github.com/xxx/package2.git #git依赖

//3. pub上面的

date_format: 1.0.6

14. 手势识别


  • 底层原始指针事件: 用户的触摸数据,如手指接触屏幕 PointerDownEvent、手指在屏幕上移动 PointerMoveEvent、手指抬起 PointerUpEvent,以及触摸取消 PointerCancelEvent.

Listener(

child: Container(

color: Colors.red,//背景色红色

width: 300,

height: 300,

),

onPointerDown: (event) => print(“down $event”),//手势按下回调

onPointerMove: (event) => print(“move $event”),//手势移动回调

onPointerUp: (event) => print(“up $event”),//手势抬起回调

);

  • 冒泡分发机制: 将触摸事件交给最内层的组件去响应,事件会从这个最内层的组件开始,沿着组件树向根节点向上冒泡分发.

  • 封装了底层指针事件手势语义的Gesture,平常一般使用GestureDetector.如点击 onTap、双击 onDoubleTap、长按 onLongPress、拖拽 onPanUpdate、缩放 onScaleUpdate 等。

//红色container坐标

double _top = 0.0;

double _left = 0.0;

Stack(//使用Stack组件去叠加视图,便于直接控制视图坐标

children: [

Positioned(

top: _top,

left: _left,

child: GestureDetector(//手势识别

child: Container(color: Colors.red,width: 50,height: 50),//红色子视图

onTap: ()=>print(“Tap”),//点击回调

onDoubleTap: ()=>print(“Double Tap”),//双击回调

onLongPress: ()=>print(“Long Press”),//长按回调

onPanUpdate: (e) {//拖动回调

setState(() {

//更新位置

_left += e.delta.dx;

_top += e.delta.dy;

});

},

),

)

],

);

  • 事件处理机制: Flutter会使用手势竞技场来进行各个手势的PK,以保证最后只有一个手势能够响应用户行为.

  • 手势冲突只是手势的语义化识别过程,对于底层指针事件是不会冲突的.

  • 父子都有点击事件的情况 因为子视图在父视图的上面,所以如果点击区域在子视图区域,子视图响应事件.

15. 跨组件共享数据


视图层级比较深的UI样式,直接通过属性传值会导致很多中间层增加冗余属性.

  • 代码太多,见demo https://github.com/xfhy/FlutterBasic/tree/master/lib/data

15.1 InheritedWidget

  • 共享父Widget的属性

15.2 Notification

  • 从下往上的数据传递,在父Widget中监听来自子Widget的事件

15.3 EventBus

  • EventBus 不依赖Widget树 这是事件总线,666

  • 遵循发布订阅 模式

15.4 对比

| 方式 | 数据流动方式 | 使用场景 |

| — | — | — |

| 属性传值 | 父到子 | 简单数据传递 |

| InheritedWidget | 父到子 | 跨层数据传递 |

| Notification | 子到父 | 状态通知 |

| EventBus | 发布订阅 | 消息批量同步 |

16. 路由管理


  • Route是页面的抽象,主要负责创建对应的界面,接收参数,响应Navigator打开和关闭

  • Navigator则会维护一个路由栈管理Route,Route打开即入栈,Route关闭即出栈.

  • 基本路由: 创建一个MaterialPageRoute实例,调用Navigator.push方法将新页面压到堆栈的顶部

class FirstScreen extends StatelessWidget {

@override

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)
img

资源分享

一线互联网面试专题

379页的Android进阶知识大全

379页的Android进阶知识大全

网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。

2020年虽然路途坎坷,都在说Android要没落,但是,不要慌,做自己的计划,学自己的习,竞争无处不在,每个行业都是如此。相信自己,没有做不到的,只有想不到的。祝大家2021年万事大吉。

本文已被CODING开源项目:《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》收录

一个人可以走的很快,但一群人才能走的更远。如果你从事以下工作或对以下感兴趣,欢迎戳这里加入程序员的圈子,让我们一起学习成长!

AI人工智能、Android移动开发、AIGC大模型、C C#、Go语言、Java、Linux运维、云计算、MySQL、PMP、网络安全、Python爬虫、UE5、UI设计、Unity3D、Web前端开发、产品经理、车载开发、大数据、鸿蒙、计算机网络、嵌入式物联网、软件测试、数据结构与算法、音视频开发、Flutter、IOS开发、PHP开发、.NET、安卓逆向、云计算

白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!**

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)
[外链图片转存中…(img-QHjkUM6N-1712569520057)]

资源分享

[外链图片转存中…(img-VBtPWFWM-1712569520057)]

[外链图片转存中…(img-wTtsQkaV-1712569520058)]

[外链图片转存中…(img-wMoMf2WR-1712569520058)]

网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。

2020年虽然路途坎坷,都在说Android要没落,但是,不要慌,做自己的计划,学自己的习,竞争无处不在,每个行业都是如此。相信自己,没有做不到的,只有想不到的。祝大家2021年万事大吉。

本文已被CODING开源项目:《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》收录

一个人可以走的很快,但一群人才能走的更远。如果你从事以下工作或对以下感兴趣,欢迎戳这里加入程序员的圈子,让我们一起学习成长!

AI人工智能、Android移动开发、AIGC大模型、C C#、Go语言、Java、Linux运维、云计算、MySQL、PMP、网络安全、Python爬虫、UE5、UI设计、Unity3D、Web前端开发、产品经理、车载开发、大数据、鸿蒙、计算机网络、嵌入式物联网、软件测试、数据结构与算法、音视频开发、Flutter、IOS开发、PHP开发、.NET、安卓逆向、云计算

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐