中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

如何在Flutter中自定義Widget

發布時間:2021-06-08 17:04:53 來源:億速云 閱讀:479 作者:Leah 欄目:移動開發

這篇文章給大家介紹如何在Flutter中自定義Widget,內容非常詳細,感興趣的小伙伴們可以參考借鑒,希望對大家能有所幫助。

在Flutter實際開發中,大家可能會遇到flutter框架中提供的widget達不到我們想要的效果,這時就需要我們去自定義widget,從Flutter構建、布局、繪制三部曲中我們了解到,實際的測量、布局、繪制操作都在RenderObject中,我們是可以進行繼承相關的RenderObject來實現自定義的。但是其實flutter框架在設計之初就給我們預留出了自定義的入口,方便我們進行自定義。

CustomPaint自定義繪制

例:圓形進度條

如何在Flutter中自定義Widget

思路:使用CustomPaint繪制需要的效果

class CircleProgress extends StatelessWidget {
 final Size size;
 final double progress;

 CircleProgress({@required this.size, @required this.progress});

 @override
 Widget build(BuildContext context) {
 return CustomPaint(
 size: size,
 painter: CircleProgressPainter(endDegree: progress * 360),//在Painter中寫真正的繪畫邏輯
 );
 }
}

class CircleProgressPainter extends CustomPainter {
 ...省略

 @override
 void paint(Canvas canvas, Size size) {
 ...繪制的具體邏輯,size是畫布的大小
 }
}

CustomSingleChildLayout對單一child進行布局

例:實現對child約束成正方形

如何在Flutter中自定義Widget

思路:使用CustomSingleChildLayout對child進行布局,并約束為正方形

class RectLayout extends StatelessWidget {
 final Widget child;

 RectLayout({@required this.child});

 @override
 Widget build(BuildContext context) {
 return CustomSingleChildLayout(
 delegate: RectLayoutDelegate(),//進行布局的代理
 child: child,
 );
 }
}

class RectLayoutDelegate extends SingleChildLayoutDelegate {
 //確定layout的size,constraints是parent傳過來的約束
 @override
 Size getSize(BoxConstraints constraints) => super.getSize(constraints);

 ///是否需要relayout
 @override
 bool shouldRelayout(SingleChildLayoutDelegate oldDelegate) => false;

 ///確定child的位置,返回一個相對于parent的偏移值,size是layout的大小,由getsize確定,childSize由getConstraintsForChild得出的Constraints對child進行約束,得到child自身的size
 @override
 Offset getPositionForChild(Size size, Size childSize) {
 double dx = (size.width - childSize.width) / 2;
 double dy = (size.height - childSize.height) / 2;
 return Offset(dx, dy);
 }

 ///確定child的約束,用于確定child的大小
 @override
 BoxConstraints getConstraintsForChild(BoxConstraints constraints) {//
 double maxEdge = min(constraints.maxWidth, constraints.maxHeight);
 return BoxConstraints(maxWidth: maxEdge, maxHeight: maxEdge);
 }
}

CustomSingleChildLayout對多個child進行布局

例:實現網格布局

如何在Flutter中自定義Widget

思路:使用CustomSingleChildLayout對child進行布局、定位,使其成為網格的布局

class GridLayout extends StatelessWidget {
 final List<Widget> children;
 final double horizontalSpace;
 final double verticalSpace;

 GridLayout(
 {@required this.children,
 @required this.horizontalSpace,
 @required this.verticalSpace});

 @override
 Widget build(BuildContext context) {
 List<Widget> layoutChildren = new List();
 for (int index = 0; index < children.length; index++) {
 layoutChildren.add(LayoutId(id: index, child: children[index]));
 }
 return CustomMultiChildLayout(
 delegate: GridLayoutDelegate(//真正的布局實現
 horizontalSpace: horizontalSpace,
 verticalSpace: verticalSpace,
 ),
 children: layoutChildren,
 );
 }
}

class GridLayoutDelegate extends MultiChildLayoutDelegate {
 final double horizontalSpace;
 final double verticalSpace;
 List<Size> _itemSizes = List();

 GridLayoutDelegate(
 {@required this.horizontalSpace, @required this.verticalSpace});

 @override
 void performLayout(Size size) {
	//對每個child進行逐一布局
 int index = 0;
 double width = (size.width - horizontalSpace) / 2;
 var itemConstraints = BoxConstraints(
 minWidth: width, maxWidth: width, maxHeight: size.height);
 while (hasChild(index)) {
 _itemSizes.add(layoutChild(index, itemConstraints));
 index++;
 }
	//對每一個child逐一進行定位
 index = 0;
 double dx = 0;
 double dy = 0;
 while (hasChild(index)) {
 positionChild(index, Offset(dx, dy));
 dx = index % 2 == 0 ? width + horizontalSpace : 0;
 if (index % 2 == 1) {
 double maxHeight =
  max(_itemSizes[index].height, _itemSizes[index - 1].height);
 dy += maxHeight + verticalSpace;
 }
 index++;
 }
 }

 @override
 bool shouldRelayout(MultiChildLayoutDelegate oldDelegate) {
 return oldDelegate != this;
 }

 //確定layout的size,constraints是parent傳過來的約束
 @override
 Size getSize(BoxConstraints constraints) => super.getSize(constraints);
}

組合自定義

一般情況,組合自定義應該是我們最經常用的方式,通過繼承自StatelessWidget或StatefulWidget,把多個Widget組合起來,從而達到我們需要的效果。

例:下拉刷新,上拉加載

實現一:通過自帶的RefreshIndictor和ScrollController組合實現

如何在Flutter中自定義Widget

思路:通過對滾動進行監聽來觸發加載更多

_scrollController.addListener(() {
 var maxScroll = _scrollController.position.maxScrollExtent;
 if (_scrollController.offset >= maxScroll) {
 if (widget.loadMoreStatus != LoadMoreStatus.noData) {
 widget.onLoadMore();
 }
 }
});

實現二:通過NotificationListener監聽scroll的整體狀態,讓后結合平移、動畫來實現

如何在Flutter中自定義Widget

思路:通過監聽用戶overscroll的距離來平移內容區域,從而達到下拉刷新,上拉加載的效果

@override
Widget build(BuildContext context) {
 double topHeight =
 _pullDirection == PullDirection.DOWN ? _overScrollOffset.dy.abs() : 0;
 double bottomHeight =
 _pullDirection == PullDirection.UP ? _overScrollOffset.dy.abs() : 0;
 return Stack(
 children: <Widget>[
 widget.headerBuilder.buildTip(_state, topHeight),
 Align(
 alignment: Alignment.bottomCenter,
 child: widget.footerBuilder.buildTip(_state, bottomHeight),
 ),
 Transform.translate(
 offset: _overScrollOffset,
 child: NotificationListener<ScrollNotification>(
  onNotification: handleScrollNotification,
  child: DecoratedBox(
  decoration: BoxDecoration(color: Colors.grey[100]),
  child: ListView.builder(
  itemBuilder: buildItem,
  itemCount: 30,
  ),
  ),
 ),
 )
 ],
 );
}

例:上下左右滑動的layout

實現:通過GestureDetector監聽手勢滑動,然后通過平移來達到效果

如何在Flutter中自定義Widget

思路:主要處理滑動邊界,以及開關的零界點

@override
Widget build(BuildContext context) {
 //debugPrint('_slideOffset:${_slideOffset.toString()}');
 return GestureDetector(
 onPanUpdate: handlePanUpdate,
 onPanEnd: handlePanEnd,
 child: Stack(
 children: <Widget>[
 widget.background,
 Transform.translate(
  child: widget.foreground,
  offset: _slideOffset,
 ),
 ],
 ),
 );
}

關于如何在Flutter中自定義Widget就分享到這里了,希望以上內容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

苏尼特左旗| 和顺县| 广水市| 如皋市| 晴隆县| 那坡县| 余姚市| 青阳县| 阿尔山市| 陵水| 永泰县| 青川县| 库尔勒市| 榆社县| 颍上县| 邓州市| 江达县| 景泰县| 繁昌县| 奉化市| 承德县| 巴彦淖尔市| 伽师县| 竹北市| 包头市| 轮台县| 田阳县| 东阿县| 扎赉特旗| 进贤县| 海原县| 陆丰市| 无棣县| 广西| 枞阳县| 礼泉县| 甘南县| 垫江县| 平陆县| 兴仁县| 曲松县|