普通视图

发现新文章,点击刷新页面。
昨天以前首页

Flutter组件封装:Sliver 中的 Container 对应组件NSliverContainer

作者 SoaringHeart
2026年4月10日 14:39

一、需求来源

项目中遇到一些代 Sliver 老代码,需要经常调整显示样式,感觉特别麻烦就想封装一个 Container 那样功能比较丰富的基础组件。

const NSliverContainer({
  super.key,
  required this.sliver,
  this.margin,
  this.padding,
  this.decoration,
  this.foregroundPadding,
  this.foregroundDecoration,
  this.opacity,
  this.ignoring,
  this.offstage,
});

/// 外边距
final EdgeInsetsGeometry? margin;

/// 内边距
final EdgeInsetsGeometry? padding;

/// 背景装饰器
final Decoration? decoration;

/// 前景装饰器内间距
final EdgeInsetsGeometry? foregroundPadding;

/// 前景装饰器
final Decoration? foregroundDecoration;

/// 透明度
final double? opacity;

/// 是否忽略事件
final bool? ignoring;

/// 是否 offstage
final bool? offstage;

simulator_screenshot_337523A2-F925-465B-B63C-A6BC1368526B.png_副本.png

二、使用示例

Widget buildSliverContainer() {
  return NSliverContainer(
    margin: const EdgeInsets.all(8),
    padding: const EdgeInsets.all(8),
    decoration: BoxDecoration(
      color: Colors.purple[50],
      borderRadius: const BorderRadius.all(Radius.circular(8)),
      border: Border.all(color: Colors.blue),
    ),
    foregroundPadding: const EdgeInsets.all(8),
    foregroundDecoration: BoxDecoration(
      color: Colors.green.withOpacity(0.6),
      borderRadius: const BorderRadius.all(Radius.circular(24)),
      border: Border.all(color: Colors.blue),
      image: DecorationImage(
        image: AssetImage(Assets.imagesBgJiguang),
      ),
    ),
    // opacity: 0.3,
    // offstage: true,
    sliver: SliverPadding(
      padding: const EdgeInsets.all(0.0),
      sliver: SliverList.list(
        children: [
          Text("NSliverContainer"),
          Text("NSliverContainer1"),
          Text("NSliverContainer2"),
        ],
      ),
    ),
  );
}

三、源码 NSliverContainer

import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';

/// sliver 族 Container
class NSliverContainer extends StatelessWidget {
  const NSliverContainer({
    super.key,
    required this.sliver,
    this.margin,
    this.padding,
    this.decoration,
    this.foregroundPadding,
    this.foregroundDecoration,
    this.opacity,
    this.ignoring,
    this.offstage,
  });

  /// 外边距
  final EdgeInsetsGeometry? margin;

  /// 内边距
  final EdgeInsetsGeometry? padding;

  /// 背景装饰器
  final Decoration? decoration;

  /// 前景装饰器内间距
  final EdgeInsetsGeometry? foregroundPadding;

  /// 前景装饰器
  final Decoration? foregroundDecoration;

  /// 透明度
  final double? opacity;

  /// 是否忽略事件
  final bool? ignoring;

  /// 是否 offstage
  final bool? offstage;

  /// 子组件
  final Widget sliver;

  @override
  Widget build(BuildContext context) {
    Widget current = sliver;

    /// padding
    if (padding != null) {
      current = SliverPadding(
        padding: padding!,
        sliver: current,
      );
    }

    if (foregroundDecoration != null) {
      current = DecoratedSliver(
        decoration: foregroundDecoration!,
        position: DecorationPosition.foreground,
        sliver: current,
      );
    }

    if (foregroundPadding != null) {
      current = SliverPadding(
        padding: foregroundPadding!,
        sliver: current,
      );
    }

    /// decoration
    if (decoration != null) {
      current = DecoratedSliver(
        decoration: decoration!,
        sliver: current,
      );
    }

    /// margin(最外层)
    if (margin != null) {
      current = SliverPadding(
        padding: margin!,
        sliver: current,
      );
    }

    if (opacity != null) {
      current = SliverOpacity(
        opacity: opacity!,
        sliver: current,
      );
    }

    if (ignoring != null) {
      current = SliverIgnorePointer(
        ignoring: ignoring!,
        sliver: current,
      );
    }

    if (offstage != null) {
      current = SliverOffstage(
        offstage: offstage!,
        sliver: current,
      );
    }

    return current;
  }

  @override
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
    properties.add(DiagnosticsProperty<EdgeInsetsGeometry>('margin', margin, defaultValue: null));
    properties.add(DiagnosticsProperty<EdgeInsetsGeometry>('padding', padding, defaultValue: null));
    properties.add(DiagnosticsProperty<Decoration>('bg', decoration, defaultValue: null));
    properties.add(DiagnosticsProperty<Decoration>('fg', foregroundDecoration, defaultValue: null));
    properties.add(DiagnosticsProperty<double>('opacity', opacity, defaultValue: null));
    properties.add(DiagnosticsProperty<bool>('ignoring', ignoring, defaultValue: null));
    properties.add(DiagnosticsProperty<bool>('offstage', offstage, defaultValue: null));
  }
}

最后、总结

1、NSliverContainer 基于 SliverPadding、 DecoratedSliver、 SliverOpacity、 SliverIgnorePointer、 SliverOffstage 等 sliver 官方组件组合而成,可以放心使用。

github

Flutter调试组件:打印任意组件尺寸位置信息 NRenderBox

作者 SoaringHeart
2026年2月28日 22:20

一、需求来源

当页面元素特别多,比较杂,又必须获取某个组件尺寸位置时,一个个加 GlobalKey 有太麻烦,这是使用一个封装好的组件就特别有用了。然后就有了 NRenderBox 组件,可以打印出子组件的位置及尺寸。

二、使用

NRenderBox(
  child: Container(
    padding: EdgeInsets.symmetric(horizontal: 8, vertical: 4),
    decoration: BoxDecoration(
      color: Colors.transparent,
      border: Border.all(color: Colors.blue),
      borderRadius: BorderRadius.all(Radius.circular(0)),
    ),
    child: Column(
      mainAxisSize: MainAxisSize.min,
      children: [
        NNetworkImage(
          width: 50,
          height: 60,
          url: AppRes.image.urls.random ?? '',
        ),
        Text("选项"),
      ],
    ),
  ),
)
flutter: NRenderBox rect: Rect.fromLTRB(88.5, 322.0, 157.5, 413.0)

三、NRenderBox源码

import 'package:flutter/material.dart';

/// 点击打印尺寸
class NRenderBox extends StatefulWidget {
  const NRenderBox({
    super.key,
    required this.child,
  });

  final Widget child;

  @override
  State<NRenderBox> createState() => _NRenderBoxState();
}

class _NRenderBoxState extends State<NRenderBox> {
  final renderKey = GlobalKey();

  RenderBox? get renderBox {
    final ctx = renderKey.currentContext;
    if (ctx == null) {
      return null;
    }
    final box = ctx.findRenderObject() as RenderBox?;
    return box;
  }

  Offset? get renderPosition {
    return renderBox?.localToGlobal(Offset.zero);
  }

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      key: renderKey,
      onTap: () {
        if (renderBox == null) {
          return;
        }
        final position = renderPosition;
        final size = renderBox!.size;
        final rect = Rect.fromLTWH(position!.dx, position.dy, size.width, size.height);
        debugPrint("$widget rect: $rect");
      },
      child: widget.child,
    );
  }
}

github

❌
❌