一統天下 flutter - widget 自定義: 通過 SingleChildRenderObjectWidget 實現自定義組件
示例如下:
lib\widget\custom\single_child_render_object_widget.dart
/*
* 通過 SingleChildRenderObjectWidget 實現自定義組件
*
* RenderBox 繼承自 RenderObject,其用於在屏幕上繪製內容
* SingleChildRenderObjectWidget 繼承自 RenderObjectWidget
* SingleChildRenderObjectWidget 是隻有一個 child 的 widget,其可以通過重寫 createRenderObject() 讓指定的 RenderObject 繪製內容
*/
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
class SingleChildRenderObjectWidgetDemo extends StatefulWidget {
const SingleChildRenderObjectWidgetDemo({Key? key}) : super(key: key);
@override
_SingleChildRenderObjectWidgetDemoState createState() => _SingleChildRenderObjectWidgetDemoState();
}
class _SingleChildRenderObjectWidgetDemoState extends State<SingleChildRenderObjectWidgetDemo> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text("title"),),
backgroundColor: Colors.orange,
body: Container(
color: Colors.red,
/// 使用自定義組件
child: const ShadowBox(
child: Icon(
Icons.accessibility,
color: Colors.black,
size: 200,
),
shadowOffset: Offset(60, 60),
),
),
);
}
}
/// 繼承 SingleChildRenderObjectWidget 實現一個自帶陰影效果的自定義組件
class ShadowBox extends SingleChildRenderObjectWidget {
/// 陰影的偏移量
final Offset shadowOffset;
const ShadowBox({super.key, super.child, required this.shadowOffset});
/// 創建一個 RenderObject 用於繪製內容
/// 注:這個 createRenderObject() 方法是由對應的 Element 調用的
@override
RenderObject createRenderObject(BuildContext context) {
return RenderShadowBox(shadowOffset: shadowOffset);
}
/// 更新 RenderObject 以便重繪
/// 注:這個 updateRenderObject() 方法是由對應的 Element 調用的
@override
void updateRenderObject(BuildContext context, covariant RenderShadowBox renderObject) {
renderObject.shadowOffset = shadowOffset;
}
}
/// 繼承 RenderBox 實現自定義繪製邏輯,並通過 with RenderObjectWithChildMixin 簡化開發
class RenderShadowBox extends RenderBox with RenderObjectWithChildMixin {
/// 陰影的偏移量
late Offset shadowOffset;
RenderShadowBox({required this.shadowOffset});
/// 佈局
@override
void performLayout() {
/// 佈局 child
/// parentUsesSize 爲 true 則自己可以獲取到 child 的尺寸
/// parentUsesSize 爲 false 則自己獲取不到 child 的尺寸,也就是說自己不需要因 child 的改變而重新佈局,從而提高效率
child!.layout(constraints, parentUsesSize: true);
/// 設置自己的尺寸與 child 相同(需要 child 在 layout() 的時候設置 parentUsesSize 爲 true)
size = (child as RenderBox).size;
}
/// 繪製
/// 注:繪製是不受佈局限制的,也就是說允許在佈局之外繪製
@override
void paint(PaintingContext context, Offset offset) {
/// 在指定的位置繪製 child
context.paintChild(child!, offset);
/// 在指定的位置添加一個不透明度爲 0x40 的圖層
context.pushOpacity(offset, 0x40, (context, offset) {
/// 在這個圖層內的指定位置繪製 child,從而實現陰影效果
context.paintChild(child!, shadowOffset);
});
}
}