buildLayer method

Widget buildLayer(
  1. BuildContext context,
  2. List<MorphGlassSurface> surfaces, {
  3. Widget? content,
  4. List<Rect> contentSlots = const [],
  5. double spacing = 0,
  6. MorphGlassOutline? outline,
})

Builds one glass layer of a control: its surfaces, back to front, in a box that fills the layer, with the control's content over them.

The surfaces of one call belong to one control and move together, so a painter can render them as a unit: share one backdrop sample, fuse neighbors, or show content through a lens. The default places each surface at its bounds, from buildSurface when it is glass and from buildFill when it is not, and content over them.

contentSlots are the boxes of the items of content - the segments of a segmented control, the tabs of a tab bar - in the layer's local coordinates, each item centered in its box. They stay put while a lens moves over them. A painter that magnifies the content seen through a lens scales each item about the center of its own slot and clips it to that slot, so a label under a moving lens grows in place and never slides; only the lens window moves. Empty means the content is one item that fills the layer.

A positive spacing makes the glass surfaces one glass container with that spacing, as UIKit's UIGlassContainerEffect and SwiftUI's GlassEffectContainer do: two surfaces closer than spacing lean toward each other and share a body when their gap is less than spacing - 0.5, by the merge law of the skin (whose blend width the spacing is, 1:1); surfaces spacing or more apart do not touch. Zero keeps every surface separate. The bars pass 12, the spacing UIKit gives the glass container of a navigation bar and of a toolbar, which is also the gap between two groups of one bar: resting groups never fuse, moving ones fuse while they pass closer.

A non-null outline is the silhouette the control has already fused its glass surfaces into, in the layer's local coordinates: the glass surfaces are one body whose edge is outline, not their own shapes (a menu joined to its button by a neck). A painter draws that body with buildBody and does not fuse the surfaces again; their shapes and kinds still describe the parts of the body. The default draws buildBody in place of buildSurface for every glass surface.

Implementation

Widget buildLayer(
  BuildContext context,
  List<MorphGlassSurface> surfaces, {
  Widget? content,
  List<Rect> contentSlots = const [],
  double spacing = 0,
  MorphGlassOutline? outline,
}) {
  final glass = [
    for (final surface in surfaces)
      if (surface.glass) surface,
  ];
  return Stack(
    clipBehavior: Clip.none,
    children: [
      if (outline != null && glass.isNotEmpty)
        Positioned.fill(child: buildBody(context, outline, glass)),
      for (final surface in surfaces)
        if (outline == null || !surface.glass)
          Positioned.fromRect(
            rect: surface.bounds,
            child: surface.glass
                ? buildSurface(context, surface)
                : buildFill(context, surface),
          ),
      if (content != null)
        Positioned.fill(
          key: const ValueKey<String>('content'),
          child: content,
        ),
    ],
  );
}