在开发的过程中比较常用的是ListView和GridView,但是如果是复杂一些的页面,单纯的ListView和GridView就不够用了。而Flutter提供了Sliver系列组件来实现复杂的滚动页面,这里就是学习的过程中所作的记录,都是入门级别的,比较简单。
Demo地址位于文章末尾。
下面就开始正文了。


SliverList和SliverGrid

SliverList只有一个属性:delegate,类型是SliverChildDelegate。SliverChildDelegate是一个抽象类,不能直接使用。Flutter中定义好了两个继承于SliverChildDelegate的类对象,可以直接用,分别是:SliverChildListDelegateSliverChildBuilderDelegate

先来看SliverChildListDelegate,声明如下:

SliverChildListDelegate(this.children, {...})

只有一个必填属性children,是一个类型为Widget的List集合。其他属性几乎不用,暂时忽略。跟ListView构造函数相同,会将所有的子组件一次性的全部渲染出来。
SliverChildBuilderDelegate则跟ListView.build构造函数类似,需要时才会创建,提高了性能。

const SliverChildBuilderDelegate(this.builder, {this.childCount,...})

主要参数是builder,是一个返回值为Widget的函数,原型如下:

Widget Function(BuildContext context, int index)

这个比较简单就不记录了。

SliverFixedExtentList是固定item高度的SliverList,只是比SliverList多了一个参数itemExtent来设置item高度,其用法跟SliverList一致。

SliverGrid有两个属性:delegategridDelegate。其中delegate跟上面的用法一样,gridDelegate的类型是SliverGridDelegate
SliverGridDelegate是一个抽象类,定义了子控件Layout相关的接口。Flutter中提供了两个SliverGridDelegate的子类SliverGridDelegateWithFixedCrossAxisCount和SliverGridDelegateWithMaxCrossAxisExtent,可以直接使用。下面分别介绍一下。

SliverGridDelegateWithFixedCrossAxisCount

该类实现了一个横轴方向上固定子控件数量的layout的算法,构造函数为:

SliverGridDelegateWithFixedCrossAxisCount({@required double crossAxisCount, double mainAxisSpacing = 0.0,double crossAxisSpacing = 0.0,double childAspectRatio = 1.0,
})
  • crossAxisCount:横轴子元素的数量。此属性值确定后子元素在横轴的长度就确定了,即横轴长度除以crossAxisCount的商。
  • mainAxisSpacing:主轴方向的间距。
  • crossAxisSpacing:横轴方向子元素的间距。
  • childAspectRatio:子元素在横轴长度和主轴长度的比例。由于crossAxisCount指定后,子元素横轴长度就确定了,然后通过此参数值就可以确定子元素在竖轴的长度。

子元素的大小是通过crossAxisCount和childAspectRatio两个参数决定的。

SliverGridDelegateWithMaxCrossAxisExtent

该类实现了一个横轴方向上子元素为固定最大长度的layout算法,其构造函数为:

SliverGridDelegateWithMaxCrossAxisExtent({double maxCrossAxisExtent,double mainAxisSpacing = 0.0,double crossAxisSpacing = 0.0,double childAspectRatio = 1.0,
})

maxCrossAxisExtent是子元素在横轴方向上的最大长度。之所以是最大长度而不是最终长度,这是因为子元素在横轴方向上的长度仍然是等分的。所以子元素在横轴方向上的元素个数num = 横轴长度 / maxCrossAxisExtent + 1,最终的子元素的宽度= 横轴长度 / num。其他参数和SliverGridDelegateWithFixedCrossAxisCount相同。

SliverGrid的使用方法跟GridView的使用方法保持一致。

SliverAnimatedList

SliverAnimatedList是带有动画的SliverList,先来看构造函数:

SliverAnimatedList({Key key,@required this.itemBuilder,this.initialItemCount = 0,})
  • initialItemCount:item的个数。
  • itemBuilder:是一个AnimatedListItemBuilder函数,原型如下:
Widget Function(BuildContext context, int index, Animation<double> animation)

使用SliverAnimatedList在添加或删除item的时候,需要通过一下方式来操作:

  1. 定义一个GlobalKey
GlobalKey<SliverAnimatedListState> _listKey = GlobalKey<SliverAnimatedListState>();
  1. 将key赋值给SliverAnimatedList
SliverAnimatedList(key: _listKey,initialItemCount: _list.length,itemBuilder: _buildItem,)
  1. 通过key.currentState.insertItem或key.currentState.removeItem来进行添加或删除。
_listKey.currentState.insertItem(_index);
_listKey.currentState.removeItem(_index,(context, animation) => _buildItem(context, item, animation));

_buildItem函数原型如下:

Widget _buildItem(BuildContext context, int index, Animation<double> animation) {return SizeTransition(sizeFactor: animation,child: Card(child: ListTile(title: Text('Item $index',),),),);}

如果想修改动画类型,就需要修改_buildItem中的动画方式。

SliverPersistentHeader

这个组件可以实现控件吸顶的效果。先来看构造函数:

SliverPersistentHeader({Key key,@required this.delegate,this.pinned = false,this.floating = false,})

其中pinned的效果就是控制header是否保持吸顶效果。另一个重要的属性则是delegate,它的类型是SliverPersistentHeaderDelegate,这是一个抽象类,所以要使用的话,需要自己定义一个子类。
子类需要重写4个父类的函数:

@overrideWidget build(BuildContext context, double shrinkOffset, bool overlapsContent) {// TODO: implement buildthrow UnimplementedError();}@override// TODO: implement maxExtentdouble get maxExtent => throw UnimplementedError();@override// TODO: implement minExtentdouble get minExtent => throw UnimplementedError();@overridebool shouldRebuild(SliverPersistentHeaderDelegate oldDelegate) {// TODO: implement shouldRebuildthrow UnimplementedError();}

其中build返回header显示的内容,maxExtentminExtent表示最大值和最小值,即header展开和闭合时的高度,若相同则header高度保持不变,若不同,则滚动的时候header的高度会自动在两只之间进行变化。shouldRebuild表示是否需要重新绘制,需要的话则返回true。
代码如下:

CustomScrollView(slivers: <Widget>[// makeHeader('Header Section 1'),SliverPersistentHeader(pinned: true,delegate: _SliverAppBarDelegate(// 自定义的delegateminHeight: 60.0,maxHeight: 60.0,child: Container(color: Colors.white,child: Center(child: Text('Header Section 1'),),),),),SliverGrid.count(crossAxisCount: 3,children: [Container(color: Colors.red, height: 150.0),Container(color: Colors.purple, height: 150.0),Container(color: Colors.green, height: 150.0),Container(color: Colors.orange, height: 150.0),Container(color: Colors.yellow, height: 150.0),Container(color: Colors.pink, height: 150.0),Container(color: Colors.cyan, height: 150.0),Container(color: Colors.indigo, height: 150.0),Container(color: Colors.blue, height: 150.0),],),],)

通过SliverPersistentHeader可以实现SliverAppBar的效果。

SliverAppBar

先来看构造函数:

SliverAppBar({this.flexibleSpace,this.forceElevated = false,this.expandedHeight,this.floating = false,this.pinned = false,this.snap = false,this.stretch = false,this.stretchTriggerOffset = 100.0,this.onStretchTrigger,...})

其他的参数都跟AppBar是一致的,就忽略了。其中有一些重要的参数:

  • expandedHeight:展开时AppBar的高度。
  • flexibleSpace:空间大小可变的组件。
  • floating:向上滚动时,AppBar会跟随着滑出屏幕;向下滚动时,会有限显示AppBar,只有当AppBar展开时才会滚动ListView。
  • pinned:当SliverAppBar内容滑出屏幕时,将始终渲染一个固定在顶部的收起状态AppBar。
  • snap:当手指离开屏幕时,AppBar会保持跟手指滑动方向相一致的状态,即手指上滑则AppBar收起,手指下滑则AppBar展开。
  • stretch:是否拉伸。

只有floating设置为true时,snap才可以设置为true。

FlexibleSpaceBar是Flutter提供的一个现成的空间大小可变的组件,并且处理好了title和background的过渡效果。重点说一下其中的stretchModes属性,这个是用来设置AppBar拉伸效果的,有三个枚举值,可以互相搭配使用,但是前提是stretch为true。

  • blurBackground:拉伸时使用模糊效果。
  • fadeTitle:拉伸时标题将消失。
  • zoomBackground默认值,拉伸时widget将填充额外的空间。

SliverFillRemaining和SliverFillViewport

SliverFillRemaining会自动充满视图的全部空间,通常用于slivers的最后一个。
SliverFillViewport 生成的每一个item都占满全屏。
用法都比较简单,就没有记录。
详细可查看demo。

SliverOpacity和SliverPadding

SliverOpacity用来设置子控件透明度,构造函数如下:

SliverOpacity({Key key,@required this.opacity,this.alwaysIncludeSemantics = false,Widget sliver,})

SliverPadding是设置需要padding的Sliver控件,其构造函数如下:

SliverPadding({Key key,@required this.padding,Widget sliver,})

SliverOpacity 和SliverPadding 中的sliver属性的值必须是Sliver系列的Widget。

SliverPrototypeExtentList

跟SliverList用法基本一致,但是子控件的高度是由prototypeItem的控件高度决定的。构造函数如下:

SliverPrototypeExtentList({Key key,@required SliverChildDelegate delegate,@required this.prototypeItem,})

SliverLayoutBuilder

理解有限,就不记录贻笑大方了。
可查看大佬的文章: Flutter Sliver一辈子之敌 (ExtendedList)

如果在CustomScrollView中用到了其他非Sliver系列的组件,需要使用SliverToBoxAdapter将这些组件包裹起来。

参考文章如下:
1、在Flutter中创建有意思的滚动效果 - Sliver系列
2、Slivers, Demystified

Demo地址:点击跳转


延伸

在Flutter中创建有意思的滚动效果 - Sliver系列

1. 前言

Flutter作为时下最流行的技术之一,凭借其出色的性能以及抹平多端的差异优势,早已引起大批技术爱好者的关注,甚至一些闲鱼美团腾讯等大公司均已投入生产使用。虽然目前其生态还没有完全成熟,但身靠背后的Google加持,其发展速度已经足够惊人,可以预见将来对Flutter开发人员的需求也会随之增长。

无论是为了技术尝鲜还是以后可能的工作机会,都9102年了,作为一个前端开发者,似乎没有理由不去尝试它。正是带着这样的心理,笔者也开始学习Flutter,同时建了一个用于练习的仓库,后续所有代码都会托管在上面,欢迎star,一起学习。这是我写的Flutter系列文章:

  • 用Flutter构建漂亮的UI界面 - 基础组件篇
  • Flutter滚动型容器组件 - ListView篇
  • Flutter网格型布局 - GridView篇
  • 在Flutter中使用自定义Icon

在之前的文章中,我们学习了如何使用ListViewGridView这两个滚动类型组件。今天,我们就来学习另一个滚动组件CustomScrollView及其搭配使用的Sliver系列组件。掌握了它们,你就可以做一些有趣的滚动效果啦~

2. 必备知识

在进入今天的正题之前,我们先来简单了解下今天的两个主角CustomScrollViewSliverCustomScrollViewFlutter提供的可以用来自定义滚动效果的组件,它可以像胶水一样将多个Sliver粘合在一起。

什么意思呢?举个栗子(你也可以点击这里看youtube上的一个视频):

假如页面中同时存在一个List和一个Grid,虽然它们看起来是一个整体,但是由于各自的滚动效果是分离的,所以没法保证一致的滚动效果。

而使用CustomScrollView组件作为滚动容器,SliverListSliverGrid分别替代ListGrid作为CustomScrollView的子组件,滚动效果再由CustomScrollView统一控制,这样就可以了。

其中SliverListSliverGrid就是我们前面提到的Sliver系列中的两员,除此之外,Sliver家族还有常用的几个:

  • SliverAppBar:Creates a material design app bar that can be placed in a CustomScrollView.
  • SliverPersistentHeader:Creates a sliver that varies its size when it is scrolled to the start of a viewport.
  • SliverFillRemaining:Creates a sliver that fills the remaining space in the viewport.
  • SliverToBoxAdapter:Creates a sliver that contains a single box widget.
  • SliverPadding:Creates a sliver that applies padding on each side of another sliver.

注意:由于CustomeScrollView的子组件只能是Sliver系列,所以如果你想将一个普通组件塞进CustomScrollView,那么务必将该组件用SliverToBoxAdapter包裹。

3. 热身:SliverList / SliverGrid

前面讲了那么多的概念似乎有些枯燥,接下来就让我们从最简单的一个例子入手来看看如何使用CustomScrollViewSliverList/SliverGrid

其实CustomScrollView的用法很简单,它有一个slivers属性,是一个Widget数组,将子组件都放在里面就可以了,其他的一些滚动相关的属性基本和我们之前学到的ListView差不多。

CustomScrollView(slivers: <Widget>[renderSliverA(),renderSliverB(),renderSliverC(),],
)

再来看看SliverList,它只有一个delegate属性,可以用SliverChildListDelegateSliverChildBuilderDelegate这两个类实现。前者将会一次性全部渲染子组件,后者将会根据视窗渲染当前出现的元素,其效果可以和ListViewListView.build这两个构造函数类比。

SliverList(delegate: SliverChildListDelegate(<Widget>[renderA(),renderB(),renderC(),])
)SliverList(delegate: SliverChildBuilderDelegate((context, index) => renderItem(context, index),childCount: 10,)
)

通过上面的例子我们发现SliverList的使用方式和ListView大同小异,而SliverGrid也是如此,这里就不再过多赘述,来看个两列网格的例子:

SliverGrid.count(crossAxisCount: 2,children: <Widget>[renderA(),renderB(),renderC(),renderD()]
)

接下来,就让我们通过一个实际例子将上面的三点结合在一起。

代码(完整版看这里)

final List<Color> colorList = [Colors.red,Colors.orange,Colors.green,Colors.purple,Colors.blue,Colors.yellow,Colors.pink,Colors.teal,Colors.deepPurpleAccent
];// Text组件需要用SliverToBoxAdapter包裹,才能作为CustomScrollView的子组件
Widget renderTitle(String title) {return SliverToBoxAdapter(child: Padding(padding: EdgeInsets.symmetric(vertical: 16),child: Text(title,textAlign: TextAlign.center,style: TextStyle(fontSize: 20),),),);
}CustomScrollView(slivers: <Widget>[renderTitle('SliverGrid'),SliverGrid.count(crossAxisCount: 3,children: colorList.map((color) => Container(color: color)).toList(),),renderTitle('SliverList'),SliverFixedExtentList(      // SliverList的语法糖,用于每个item固定高度的Listdelegate: SliverChildBuilderDelegate((context, index) => Container(color: colorList[index]),childCount: colorList.length,),itemExtent: 100,),],
)

效果图

上面的例子中还有一点需要注意的是:我们将标题组件放在了SliverToBoxAdapter内,因为CustomScrollView只接受Sliver系列的组件。

4. 眼前一亮的SliverAppBar

AppBar是常用来构建一个页面头部Bar的组件,在CustomScrollView中与其对应的是SliverAppBar组件。它有什么神奇之处呢?随着页面的滚动,头部Bar将会有一个收起过渡的效果。我们先来看下效果:

float效果 snap效果 pinned效果

通过上面的预览图,想必你肯定很好奇SliverAppBar中的过渡效果是如何实现的~先别急,我们先来看下应该如何使用它:

SliverAppBar(floating: true,snap: true,pinned: true,expandedHeight: 250,flexibleSpace: FlexibleSpaceBar(title: Text(this.title),background: Image.network('http://img1.mukewang.com/5c18cf540001ac8206000338.jpg',fit: BoxFit.cover,),),
)

SliverAppBar最重要的几个属性在上面的例子中罗列出来。其中:

  • expandedHeight:展开状态下appBar的高度,即图中图片所占空间;
  • flexibleSpace:空间大小可变的组件,Flutter给我们提供了一个现成的FlexibleSpaceBar组件,给我们处理好了title过渡的效果。

另外,floating/snap/pinned这三个属性可以指定SliverAppBar内容滑出屏幕之后的表现形式。

  • float:向下滑动时,即使当前CustomScrollView不在顶部,SliverAppBar也会跟着一起向下出现;
  • snap:当手指放开时,SliverAppBar会根据当前的位置进行调整,始终保持展开收起的状态;
  • pinned:不同于float效果,当SliverAppBar内容滑出屏幕时,将始终渲染一个固定在顶部的收起状态组件。

需要注意的是:snap效果一定要在floattrue时才会生效。另外,你也可以将这三者进行组合使用。

5. 花样多变的SliverPersistentHeader

在上一小节中我们见识到了SliverAppBar的神奇之处,其实它就是基于SliverPersistentHeader实现的。通过SliverPersistentHeader,我们还可以实现sticky吸顶的效果。

SliverPersistentHeader最重要的一个属性是SliverPersistentHeaderDelegate,为此我们需要实现一个类继承自SliverPersistentHeaderDelegate

class StickyTabBarDelegate extends SliverPersistentHeaderDelegate {@overridedouble get minExtent => null;@overridedouble get maxExtent => null;@overridebool shouldRebuild(SliverPersistentHeaderDelegate oldDelegate) => null;@overrideWidget build(BuildContext context, double shrinkOffset, bool overlapsContent) => null;
}

可以看到,SliverPersistentHeaderDelegate的实现类必须实现其4个方法。其中:

  • minExtent:收起状态下组件的高度;
  • maxExtent:展开状态下组件的高度;
  • shouldRebuild:类似于react中的shouldComponentUpdate
  • build:构建渲染的内容。

接下来,我们就来实现一个TabBar吸顶的效果。

代码(完整版看这里)

CustomScrollView(slivers: <Widget>[SliverAppBar(// ...),SliverPersistentHeader( // 可以吸顶的TabBarpinned: true,delegate: StickyTabBarDelegate(child: TabBar(labelColor: Colors.black,controller: this.tabController,tabs: <Widget>[Tab(text: 'Home'),Tab(text: 'Profile'),],),),),SliverFillRemaining(        // 剩余补充内容TabBarViewchild: TabBarView(controller: this.tabController,children: <Widget>[Center(child: Text('Content of Home')),Center(child: Text('Content of Profile')),],),),],
)class StickyTabBarDelegate extends SliverPersistentHeaderDelegate {final TabBar child;StickyTabBarDelegate({@required this.child});@overrideWidget build(BuildContext context, double shrinkOffset, bool overlapsContent) {return this.child;}@overridedouble get maxExtent => this.child.preferredSize.height;@overridedouble get minExtent => this.child.preferredSize.height;@overridebool shouldRebuild(SliverPersistentHeaderDelegate oldDelegate) {return true;}
}

效果图

根据上面的图我们可以看到,当下方tab内容滑出屏幕后,tabBar并没有跟着一起滑走,而是粘在了顶部。可见SliverPersistentHeader的确可以满足我们的sticky效果。

不过SliverPersistentHeader的神奇可远不止如此哦~我们可以通过它自定义一些头部的过渡效果,毕竟SliverAppBar也是通过它实现的。就比如下方这个电影详情页的头部过渡效果,这在一般的app种还是比较常见的。

那么这种效果要如何实现呢?关键就在于build方法中的shrinkOffset属性,它代表当前头部的滚动偏移量。我们可以根据它计算得到当前收起头部的背景颜色以及图标和文案的字体颜色,这样就能根据当前位置得到过渡效果啦~

代码(完整版看这里)

class SliverCustomHeaderDelegate extends SliverPersistentHeaderDelegate {final double collapsedHeight;final double expandedHeight;final double paddingTop;final String coverImgUrl;final String title;SliverCustomHeaderDelegate({this.collapsedHeight,this.expandedHeight,this.paddingTop,this.coverImgUrl,this.title,});@overridedouble get minExtent => this.collapsedHeight + this.paddingTop;@overridedouble get maxExtent => this.expandedHeight;@overridebool shouldRebuild(SliverPersistentHeaderDelegate oldDelegate) {return true;}Color makeStickyHeaderBgColor(shrinkOffset) {final int alpha = (shrinkOffset / (this.maxExtent - this.minExtent) * 255).clamp(0, 255).toInt();return Color.fromARGB(alpha, 255, 255, 255);}Color makeStickyHeaderTextColor(shrinkOffset, isIcon) {if(shrinkOffset <= 50) {return isIcon ? Colors.white : Colors.transparent;} else {final int alpha = (shrinkOffset / (this.maxExtent - this.minExtent) * 255).clamp(0, 255).toInt();return Color.fromARGB(alpha, 0, 0, 0);}}@overrideWidget build(BuildContext context, double shrinkOffset, bool overlapsContent) {return Container(height: this.maxExtent,width: MediaQuery.of(context).size.width,child: Stack(fit: StackFit.expand,children: <Widget>[// 背景图Container(child: Image.network(this.coverImgUrl, fit: BoxFit.cover)),// 收起头部Positioned(left: 0,right: 0,top: 0,child: Container(color: this.makeStickyHeaderBgColor(shrinkOffset),    // 背景颜色child: SafeArea(bottom: false,child: Container(height: this.collapsedHeight,child: Row(mainAxisAlignment: MainAxisAlignment.spaceBetween,children: <Widget>[IconButton(icon: Icon(Icons.arrow_back_ios,color: this.makeStickyHeaderTextColor(shrinkOffset, true),    // 返回图标颜色),onPressed: () => Navigator.pop(context),),Text(this.title,style: TextStyle(fontSize: 20,fontWeight: FontWeight.w500,color: this.makeStickyHeaderTextColor(shrinkOffset, false),   // 标题颜色),),IconButton(icon: Icon(Icons.share,color: this.makeStickyHeaderTextColor(shrinkOffset, true),    // 分享图标颜色),onPressed: () {},),],),),),),),],),);}
}

上面的代码虽然很长,但大部分是构建widget的代码。所以,我们重点关注makeStickyHeaderTextColormakeStickyHeaderBgColor即可。这两个方法都是根据当前的shrinkOffset值计算过渡过程中的颜色值。另外,这里需要注意头部在iPhoneX及以上的刘海头涉及,可以用SafeArea组件解决问题。

6. 总结

本文首先介绍了CustomScrollViewSliver系列组件的概念及其关系,接着以SliverListSliverGrid结合的示例说明了其使用方法。然后,又介绍了较常用的SliverAppBar组件,分别解释了其float/snap/pinned各自的效果。最后,讲解了SliverPersistentHeader组件的使用方法,并用实际例子加以说明其自定义过渡效果的用法。希望通过本文的介绍,你可以用CustomScrollViewSliver系列组件创建出更有意思的滚动效果~

Flutter Sliver系列组件入门相关推荐

  1. 【Flutter 问题系列第 5 篇】Flutter 去除 ListView 组件中的蓝色回弹效果

    这是[Flutter 问题系列第 5 篇],如果觉得有用的话,欢迎关注专栏. ListView 组件默认的滑动效果如下 可以看到,在顶部向下拖动或者到底部向上拖动时,会有一个蓝色的回弹效果. 这是因为 ...

  2. Flutter Sliver大家族之SliverList(),SliverFixedExtentList(),SliverGrid()组件②

    Flutter Sliver大家族之SliverList,SliverFixedExtentList,SliverGrid组件② SliverFixedExtentList() SliverList( ...

  3. Flutter Sliver大家族之SliverPersistentHeader()和SliverToBoxAdapter()组件(实现固定头布局)③

    Flutter Sliver大家庭之SliverPersistentHeade和SliverToBoxAdapter实现固定头布局③ SliverPersistentHeader SliverToBo ...

  4. 【Flutter 问题系列第 71 篇】Flutter 中 Uint8List 和 Image 之间的相互转换

    这是[Flutter 问题系列第 71 篇],如果觉得有用的话,欢迎关注专栏. Flutter 版本:2.5.0,Dart 版本:2.14 因为最近一直在处理照相机.裁剪图片和 OCR 图片识别的功能 ...

  5. 【Flutter 问题系列第 15 篇】如何给 Flutter 中的图片设置透明度

    这是[Flutter 问题系列第 15 篇],如果觉得有用的话,欢迎关注专栏. 在 Flutter 中,如果背景色是颜色的话,我们知道可以直接通过 Color.fromRGBO(r, g, b, op ...

  6. 【Flutter 问题系列第 41 篇】Cannot provide both a color and a decoration,To provide both, use “decoration“

    这是[Flutter 问题系列第 41 篇],如果觉得有用的话,欢迎关注专栏. 这个错误相信每一个刚接触 Flutter 的都会碰到,不是什么大问题,只是一个注意事项而已. 一:报错信息 示例代码如下 ...

  7. 【Flutter 问题系列第 22 篇】在 Flutter 中如何截取屏幕并显示到页面中,以及如何将截图保存到相册

    这是[Flutter 问题系列第 22 篇],如果觉得有用的话,欢迎关注专栏. 关于在 Flutter 中如何截取屏幕,以及如何将截图保存到相册的文章少之又少,即使有,也是错误一大片,有的甚至运行后都 ...

  8. 【Flutter 问题系列第 40 篇】如何给 Container 四周设置圆角以及给某一角设置圆角

    这是[Flutter 问题系列第 40 篇],如果觉得有用的话,欢迎关注专栏. Container 组件有一个属性 BoxDecoration ,可以用来修改样式 1.给 Container 四周设置 ...

  9. 【Flutter 问题系列第 46 篇】如何修改 Flutter 项目的包名

    这是[Flutter 问题系列第 46 篇],如果觉得有用的话,欢迎关注专栏. 如果你开发过 Android 原生的项目,修改项目的包名只需要修改 android - app - build.grad ...

  10. 【Flutter 问题系列第 19 篇】安装 apk 包时提示 Install canceled by user 的问题如何解决

    这是[Flutter 问题系列第 19 篇],如果觉得有用的话,欢迎关注专栏. 一:问题描述 公司的测试机中我用的是小米的,用着一直没问题,这不五一想着用自己的 Redmi Note 9 Pro 跑一 ...

最新文章

  1. C~K的班级_JAVA
  2. 【机器学习基础】回归相关指标优化​
  3. python 连 mongodb
  4. 48岁的C语言,你知道它背后的历史吗?
  5. .NET 中创建支持集合初始化器的类型
  6. silklabo哪个公众号有资源_微小说免费渣渣团资源公众号看大全集
  7. 【ZOJ - 3210】A Stack or A Queue? (模拟)
  8. EUCM鱼眼相机模型详解
  9. php发送sql,php学习笔记(二)php与mysql连接与用php发送SQL查询
  10. matlab麦克斯韦电磁方程组,从麦克斯韦方程组到电磁波动方程
  11. AndroidStudio 编译报错 abc_list_selector_disabled_holo_light.9.png
  12. 操作系统基础(存储管理)
  13. 骗子举报查询系统高级版源码
  14. 数据分析、数据挖掘、机器学习实习面经总结
  15. CentOS搭建SonarQube8.3
  16. 在html中表单对象以什么标记开始,HTML学习笔记——表单
  17. Windows 10家庭中文版中启用WSL 2
  18. 【每周话题第七期】你是#新生代农民工#吗?你赞同IT从业者被纳入新生代农民工吗?
  19. 轻松洁净地面,还能自动清洗烘干拖布,只需一台追觅扫拖机器人
  20. 马云成全球50大领袖人物排名第二,凭什么?

热门文章

  1. 计算机usb接口失灵,电脑usb接口全部失灵
  2. 数据有效性跨表引用的解决
  3. 子网掩码及其与IP地址、网关的关系
  4. Mybatis之choose(where,otherwise)标签
  5. QQ、微信头像照片模糊怎么办?如何处理高清?
  6. spark的数三角形算法_腾讯开源全栈机器学习平台 Angel 3.0,支持三大类型图计算算法...
  7. matlab 按照字符串运行,matlab中将字符串视为语句运行的方法——eval()函数
  8. 几个提高睡眠质量的方法。
  9. 【转】虚拟 IO 服务器(VIOS)和 IBM i
  10. python编程之条形码生成大全