一个Android菜鸟入门Flutter 笔记(一)

Point.factor = 10;

Point.printZValue();

//为空时跳过执行

Point?.printZValue();

var p2 = Point2.test(1);

}

class Test {

int _a = 1;

int b = 2;

}

class Point {

num x, y;

static num factor = 0;

//语法糖,等同于在函数体内:this.x = x;this.y = y;

Point(this.x, this.y);

void printInfo() => print(‘( x , x, x,y)’);

static void printZValue() => print(‘factor=$factor’);

}

class Point2 {

num x, y, z;

//z也得到了初始化

Point2(this.x, this.y) : z = 0;

//重定向构造函数

Point2.test(num x) : this(x, 0);

}

class Point3 {

num x = 0, y = 0;

void printInfo() => print(‘( x , x, x,y)’);

}

class Vector extends Point3 {

num z = 0;

//覆写了父类的方法

@override

void printInfo() => print(‘x = x , y = x,y= x,y=y’);

}

class Coordinate implements Point3 {

//成员变量需要重新声明

num x = 0, y = 0;

//成员函数需要重新实现

@override

void printInfo() {}

}

//混入Mixin 可以视为具有实现方法的接口

//非继承的方法,使用其他类中的变量与方法

class Coordinate2 with Point3 {}

2. Flutter区别于其他方案的关键技术


  • Flutter 使用 Native 引擎渲染视图

  • React Native 之类的框架,只是通过 JavaScript 虚拟机扩展调用系统组件,由 Android 和 iOS 系统进行组件的渲染;Flutter 则是自己完成了组件渲染的闭环。

  • UI线程使用Dart来构建视图结构数据,这些数据会在GPU线程进行图层合成,随后交给Skia引擎加工成GPU数据,而这些数据会通过OpenGL最终提供给GPU渲染.

  • 使用Dart语言,同时支持JIT(动态编译,需要用的时候编译,开发的时候)和AOT(静态编译,先编译好,正式包的时候)

  • J9Mu9S.png

3. Widget的设计思路和基本原理


  • 核心设计思想: 一切皆Widget

  • Widget是不可变的,当视图渲染的配置信息发生变化时,Flutter会选择重建Widget树的方法进行数据更新.

  • 数据驱动UI构建

  • Widget本身不涉及实际渲染位图,它是轻量级的数据结构,重建成本低.

  • Element是Widget的一个实例化对象,它承载了视图构建的上下文数据,是连接结构化的配置信息到完成最终渲染的桥梁

  • Element同时持有Widget和RenderObject,最后负责渲染的是RenderObject

  • Flutter展示过程: 布局,绘制,合成,渲染

4. State的选择


  • Widget有StatelessWidget和StatefulWidget

  • StatefulWidget对应有交互,需要动态变化视觉效果的场景,StatelessWidget则用于处理静态的,无状态的视图展示

  • Flutter的视图开发是声明式的,其核心设计思想就将视图和数据分离

  • Widget生命周期内,State中的任何更改都将强制Widget重新构建

  • StatelessWidget,如Text,Container,Row,Column等.它们一旦创建成功就不再关心,也不相应任何数据变化进行重绘

  • 避免无谓的StatefulWidget使用,可以提高Flutter应用渲染性能.

5. 生命周期


5.1 Widget 视图生命周期

  • 生命周期其实是State的

  • State生命周期分为3个阶段

  1. 创建(插入视图树)

  2. 更新(在视图树中存在)

  3. 销毁(从视图树中移除)

  • 创建
  1. 构造方法

  2. initState

  3. didChangeDependencies

  4. build

  • 更新
  1. setState->build

  2. disUpdateWidget->build

  3. didChangeDependencies->build

  • 销毁
  1. deactivate

  2. dispose

  • 构造方法: 接收父Widget传递的初始化UI配置数据

  • initState: State对象被插入视图树的时候被调用,在这里做初始化工作

  • didChangeDependencies: 处理State对象依赖关系变化,initState()调用结束后会被调用

  • build: 构建视图,在这里根据父Widget传递过来的初始化配置数据,以及State状态,创建一个Widget返回

  • setState: 当状态数据发生变化时,调用这个方法,告诉Flutter,数据变了,根据更新后的数据重建UI

  • didChangeDependencies: State对象的依赖关系发生变化时(系统语言Locale或应用主题更改),系统会通知State调用此方法

  • didUpdateWidget: 当Widget的配置发生变化时,如父Widget触发重建,热重载时,会被调用.

  • deactivate: 组件的可见状态发生变化,State会被暂时从视图树中移除. 页面切换时,上一个页面的State对象在视图树中的位置发生了变化,会先调用deactivate,再调用build.

  • dispose: 当State被永久地从视图树中移除,比如关闭页面.到这里时,组件就要销毁了,这里做最终的资源释放,移除监听,清理环境.

| 方法名 | 功能 | 调用时机 | 调用次数 |

| — | — | — | — |

| 构造方法 | 接收父Widget传递的初始化UI配置数据 | 创建State时 | 1 |

| initState | 与渲染相关的初始化工作 | 在State被插入视图树时 | 1 |

| didChangeDependencies | 处理State对象依赖关系变化 | initState后及State对象依赖关系变化时 | >=1 |

| build | 构建视图 | State准备好数据需要渲染时 | >=1 |

| setState | 触发视图重建 | 需要刷新UI时 | >=1 |

| didUpdateWidget | 处理Widget的配置变化 | 父Widget setState触发子Widget重建时 | >=1 |

| deactivate | 组件被移除 | 组件不可视 | >=1 |

| dispose | 组件被销毁 | 组件被永久移除 | 1 |

5.2 App(也是Widget) 生命周期

  • 利用WidgetsBindingObserver类

abstract class WidgetsBindingObserver {

//页面pop

Future didPopRoute() => Future.value(false);

//页面push

Future didPushRoute(String route) => Future.value(false);

//系统窗口相关改变回调,如旋转

void didChangeMetrics() { }

//文本缩放系数变化

void didChangeTextScaleFactor() { }

//系统亮度变化

void didChangePlatformBrightness() { }

//本地化语言变化

void didChangeLocales(List locale) { }

//App生命周期变化

void didChangeAppLifecycleState(AppLifecycleState state) { }

//内存警告回调

void didHaveMemoryPressure() { }

//Accessibility相关特性回调

void didChangeAccessibilityFeatures() {}

}

  • 在didChangeAppLifecycleState回调函数中,AppLifecycleState参数是枚举类,它是Flutter对App生命周期状态的封装.

  • resumed 可见的,并能响应用户输入

  • inactive: 处在不活动状态,无法处理用户响应

  • paused: 不可见并不能响应用户的输入,但是在后台继续活动中

  • 在initState中注册监听器,在dispose中移除监听器

class _MyHomePageState extends State with WidgetsBindingObserver{

@override

@mustCallSuper

void initState() {

super.initState();

WidgetsBinding.instance.addObserver(this);//注册监听器

}

@override

@mustCallSuper

void dispose(){

super.dispose();

WidgetsBinding.instance.removeObserver(this);//移除监听器

}

@override

void didChangeAppLifecycleState(AppLifecycleState state) async {

print(“$state”);

if (state == AppLifecycleState.resumed) {

//do sth

}

}

}

  • 后台(paused)切入前台: AppLifecycleState.inactive->AppLifecycleState.resumed

  • 前台(resumed)退回到后台: AppLifecycleState.inactive->AppLifecycleState.paused

  • WidgetsBingding提供了单次Frame绘制回调,以及实时Frame绘制回调两种机制.

  • 单次

WidgetsBinding.instance.addPostFrameCallback((_){

print(“单次Frame绘制回调”);//只回调一次

});

  • 实时绘制

WidgetsBinding.instance.addPersistentFrameCallback((_){

print(“实时Frame绘制回调”);//每帧都回调

});

6. 文本


  • Text,单一样式. 构造参数分为2类

  • 控制整体文本布局的参数: 对齐方式textAlign,文本排版方向textDirection,文本显示最大行数 maxLines、文本截断规则 overflow 等

  • 控制文本展示样式的参数: 统一封装到style参数中,字体名称fontFamily,字体大小fontSize,文本颜色color,文本阴影shadows等

Text(

‘文本是视图系统中的常见控件,用来显示一段特定样式的字符串,就比如Android里的TextView,或是iOS中的UILabel。’,

textAlign: TextAlign.center,//居中显示

style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20, color: Colors.red),//20号红色粗体展示

);

  • TextSpan,可展示混合样式.(类似SpannableString)

TextStyle blackStyle = TextStyle(fontWeight: FontWeight.normal, fontSize: 20, color: Colors.black); //黑色样式

TextStyle redStyle = TextStyle(fontWeight: FontWeight.bold, fontSize: 20, color: Colors.red); //红色样式

Text.rich(

TextSpan(

children: [

TextSpan(text:‘文本是视图系统中常见的控件,它用来显示一段特定样式的字符串,类似’, style: redStyle), //第1个片段,红色样式

TextSpan(text:‘Android’, style: blackStyle), //第1个片段,黑色样式

TextSpan(text:‘中的’, style:redStyle), //第1个片段,红色样式

TextSpan(text:‘TextView’, style: blackStyle) //第1个片段,黑色样式

]),

textAlign: TextAlign.center,

);

7. 图片


  • Image

  • 加载本地资源图片,如 Image.asset(‘images/logo.png’);

  • 加载本地(File 文件)图片,如 Image.file(new File(’/storage/xxx/xxx/test.jpg’));

  • 加载网络图片,如 Image.network(‘http://xxx/xxx/test.gif’)

  • 填充模式fit,拉伸 centerSlice,重复模式repeat

  • Image通过内部ImageProvider根据缓存状态,触发异步加载流程,通知_imageState(Image这种控件肯定不是静态的撒,得需要一个State)刷新UI.

  • FadeInImage,可以提供占位图,加载动画等.

FadeInImage.assetNetwork(

placeholder: ‘assets/loading.gif’, //gif占位

image: ‘https://xxx/xxx/xxx.jpg’,

fit: BoxFit.cover, //图片拉伸模式

width: 200,

height: 200,

)

  • 图片默认缓存到内存,LRU(最近最少使用),如需缓存到本地则需要使用第三方的CachedNetworkImage(还提供了错误展示图片)控件

8. 按钮


  • FloatingActionButton 圆形按钮

  • RaisedButton,凸起的按钮,和Android默认的Button长得一样丑

  • FlatButton,扁平的按钮,默认透明背景,被点击后呈现灰色背景

FloatingActionButton(onPressed: () => print(‘FloatingActionButton pressed’),child: Text(‘Btn’),);

FlatButton(onPressed: () => print(‘FlatButton pressed’),child: Text(‘Btn’),);

RaisedButton(onPressed: () => print(‘RaisedButton pressed’),child: Text(‘Btn’),);

  • onPressed参数用于设置回调,如果参数为空,则按钮会被禁用

  • child参数用于控制控件长什么样子

  • 其他丰富api

FlatButton(

color: Colors.yellow, //设置背景色为黄色

shape:BeveledRectangleBorder(borderRadius: BorderRadius.circular(20.0)), //设置斜角矩形边框

colorBrightness: Brightness.light, //确保文字按钮为深色

onPressed: () => print(‘FlatButton pressed’),

child: Row(children: [Icon(Icons.add), Text(“Add”)],)

);

  • Button都是由RawMaterialButton承载视觉,Image都是RawImage,Text是RichText。它们都继承自RenderObjectWidget,而RenderObjectWidget的父类就是Widget。

9. ListView


9.1 ListView

  • 同时支持垂直方向和水平方向滚动

  • 创建子视图方式

| 构造函数名 | 特点 | 适用场景 | 适用频次 |

| — | — | — | — |

| ListView | 一次性创建好全部子Widget | 适用于展示少量连续子Widget的场景 | 中 |

| ListView.builder | 提供子Widget创建方法,仅在需要展示的时候才创建 | 适用于子Widget较多,且视觉效果呈现某种规律性的场景 | 高 |

| ListView.separated | 与ListView.builder类似,并提供了自定义分割线的功能 | 与ListView.builder场景类似 | 中 |

  • 第一种 ListView 直接构建

ListView(

children: [

//设置ListTile组件的标题与图标

ListTile(leading: Icon(Icons.map), title: Text(‘Map’)),

ListTile(leading: Icon(Icons.mail), title: Text(‘Mail’)),

ListTile(leading: Icon(Icons.message), title: Text(‘Message’)),

]);

  • 第二种 ListView.builder.itemExtent 并不是一个必填参数。但,对于定高的列表项元素,我强烈建议你提前设置好这个参数的值。

ListView.builder(

//itemCount,表示列表项的数量,如果为空,则表示 ListView 为无限列表

itemCount: 100, //元素个数

itemExtent: 50.0, //列表项高度

itemBuilder: (BuildContext context, int index) => ListTile(title: Text(“title $index”), subtitle: Text(“body $index”))

);

  • 第三种 ListView.separated

//使用ListView.separated设置分割线

ListView.separated(

itemCount: 100,

separatorBuilder: (BuildContext context, int index) => index %2 ==0? Divider(color: Colors.green) : Divider(color: Colors.red),//index为偶数,创建绿色分割线;index为奇数,则创建红色分割线

itemBuilder: (BuildContext context, int index) => ListTile(title: Text(“title $index”), subtitle: Text(“body $index”))//创建子Widget

)

9.2 CustomScrollView

  • CustomScrollView是用来处理多个需要自定义滑动效果的Widget.在CustomScrollView中,这些彼此独立的,可滑动的Widget被统称为Sliver.

  • 比如ListView 的 Sliver 实现为 SliverList,AppBar 的 Sliver 实现为 SliverAppBar

  • 这些Sliver不再维护各自的滚动状态,交由CustomScrollView统一管理,最终实现滑动效果的一致性

CustomScrollView(

slivers: [

SliverAppBar(//SliverAppBar作为头图控件

title: Text(‘CustomScrollView Demo’),//标题

floating: true,//设置悬浮样式

flexibleSpace: Image.network(“https://xx.jpg”,fit:BoxFit.cover),//设置悬浮头图背景

expandedHeight: 300,//头图控件高度

),

SliverList(//SliverList作为列表控件

delegate: SliverChildBuilderDelegate(

(context, index) => ListTile(title: Text(‘Item #$index’)),//列表项创建方法

childCount: 100,//列表元素个数

),

),

]);

9.3 ScrollController

  • ScrollController用于对ListView进行滚动信息的监听,以及相应的滚动控制.

class MyControllerAppState extends State {

//ListView控制器

ScrollController _controller;

//标识目前是否需要启用top按钮

bool isToTop = false;

@override

void initState() {

_controller = ScrollController();

_controller.addListener(() {

//ListView向下滚动1000 则启用top按钮

if (_controller.offset > 1000) {

setState(() {

isToTop = true;

});

} else if (_controller.offset < 300) {

//向下滚动不足300,则禁用按钮

setState(() {

isToTop = false;

});

}

});

super.initState();

}

@override

Widget build(BuildContext context) {

return MaterialApp(

home: Scaffold(

body: ListView.builder(

//将控制器传入

controller: _controller,

itemCount: 100,

itemExtent: 100,

itemBuilder: (context, index) =>

ListTile(title: Text(‘index $index’))),

floatingActionButton: RaisedButton(

//如果isToTop是true则滑动到顶部,否则禁用按钮

onPressed: isToTop

? () {

//滑动到顶部

_controller.animateTo(0.0,

duration: Duration(microseconds: 200),

curve: Curves.ease);

}
null,

child: Text(‘top’),

),

),

);

}

@override

void dispose() {

_controller.dispose();

super.dispose();

}

}

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年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

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

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

img

img

img

img

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

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

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

分享读者

作者2013年java转到Android开发,在小厂待过,也去过华为,OPPO等大厂待过,18年四月份进了阿里一直到现在。

被人面试过,也面试过很多人。深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长,而且极易碰到天花板技术停滞不前!

我们整理了一份阿里P7级别的Android架构师全套学习资料,特别适合有3-5年以上经验的小伙伴深入学习提升。

主要包括腾讯,以及字节跳动,阿里,华为,小米,等一线互联网公司主流架构技术。

腾讯T3架构师学习专题资料

如果你觉得自己学习效率低,缺乏正确的指导,可以一起学习交流!

我们致力打造一个平等,高质量的Android交流圈子,不一定能短期就让每个人的技术突飞猛进,但从长远来说,眼光,格局,长远发展的方向才是最重要的。

35岁中年危机大多是因为被短期的利益牵着走,过早压榨掉了价值,如果能一开始就树立一个正确的长远的职业规划。35岁后的你只会比周围的人更值钱。

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

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

目录如下

assets

├── background.jpg //1.0x图

├── 2.0x

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

└── 3.0x

在pubspec.yaml文件声明:

flutter:

assets:

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

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

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

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

[外链图片转存中…(img-TjLH0sIe-1713442713656)]

[外链图片转存中…(img-Rq3UfPNR-1713442713657)]

[外链图片转存中…(img-UgETTiny-1713442713658)]

[外链图片转存中…(img-LxWPQJgQ-1713442713659)]

[外链图片转存中…(img-YDKPV2E6-1713442713660)]

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

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

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

分享读者

作者2013年java转到Android开发,在小厂待过,也去过华为,OPPO等大厂待过,18年四月份进了阿里一直到现在。

被人面试过,也面试过很多人。深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长,而且极易碰到天花板技术停滞不前!

我们整理了一份阿里P7级别的Android架构师全套学习资料,特别适合有3-5年以上经验的小伙伴深入学习提升。

主要包括腾讯,以及字节跳动,阿里,华为,小米,等一线互联网公司主流架构技术。

[外链图片转存中…(img-KXoY2WhP-1713442713661)]

如果你觉得自己学习效率低,缺乏正确的指导,可以一起学习交流!

我们致力打造一个平等,高质量的Android交流圈子,不一定能短期就让每个人的技术突飞猛进,但从长远来说,眼光,格局,长远发展的方向才是最重要的。

35岁中年危机大多是因为被短期的利益牵着走,过早压榨掉了价值,如果能一开始就树立一个正确的长远的职业规划。35岁后的你只会比周围的人更值钱。

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

  • 21
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值