Skip to content

Releases: PlugFox/g1455

v0.3.0

Choose a tag to compare

@PlugFox PlugFox released this 19 Oct 06:38
38d2277

A minor version rather than a patch, for two changes in behaviour: under an
adaptive host the tab bar's accessory now wears the bar's branch instead of
its own, and GlassLoad counts only glass that draws. Existing code builds
unchanged.

Adaptive glass:

  • The tab bar adapts, as one control. It did already, by accident, because
    the bar is a GlassBar, while the guide said it did not; its accessory, a
    GlassBar of its own, read its own backdrop, so a light bar could sit
    under a dark accessory as rows scrolled by. The accessory now wears the
    bar's verdict, and the two cross between branches together. The guide,
    the tab bar page and the README say so (#10).
  • The GlassBackdropReading example, the site's "Content on the glass" and
    the skill picked an icon colour from reading.brightness, which switches
    at a different level from the glass: over a mid grey in the light
    appearance that is a white icon on light glass. They give
    GlassTheme.of(context).legibility().label for a colour now, and keep
    brightness for content that follows the backdrop itself (#11).

Cost:

  • GlassLedger.read leaves glass that draws nothing out of the load. A
    control's drop at rest is a registered surface at materialize 0; it
    paints no glass and the host captured nothing for it, yet the ledger
    counted it, so twenty resting switches read as twenty captured surfaces
    and twenty surfaces of fragmentation excess. They are
    GlassLoad.restingSurfaceCount now, and in none of the other counts or
    areas (#12).
  • GlassSurfaceRecord.draws and GlassSurfaceRecord.captured: the one test
    the host's capture and the ledger both use, where each kept its own.

Fixes:

  • A GlassSurface removed under a held finger no longer asserts on the rest
    of the gesture: the binding keeps routing it to the targets of the down's
    hit test, and the surface transformed the position while detached (#13,
    by @melo936).
  • GlassTabBar and GlassSegmentedControl removed under a held finger — a
    route popped, a list recycled the row — ignore the rest of the gesture.
    They moved animation controllers they had disposed, which asserts in debug
    and throws a null check in release. The button, switch, slider, stepper
    and page control are tested the same way and were already safe.
  • A tab bar's accessory beside the collapsed circle keeps its lower rim. On
    the side-by-side layout the bar is 44 tall, and a 48-tall capsule centred
    on the circle hung 2 px past the bar's box, which clipped its bottom edge
    and corners. Beside the circle it is no taller than the circle now (#9).
  • A surface that replaces another at the same rect draws. The host's
    oracle watches rectangles and a frame finds its slots by identity, so a
    swap that moved no rectangle, behind a repaint boundary of its own, held
    the capture and the newcomer drew nothing until something else changed —
    GlassMorph settling into a plain surface did. The host now retakes when
    the set of surfaces it captures for changes. The same check catches glass
    that comes back to the top rung at the rects it left.
  • The capture walk counts a colour filter under an OpacityLayer that
    arrives through pushLayer, as it did under a composited one, and sends
    that subtree to the stock capture.
  • FilteringCanvas drops a mask-filtered paint on every draw that takes a
    paint — points, images, vertices, atlases — not only on the shapes, as
    dropMaskFiltered is documented to.
  • GlassMenuAnchor, GlassPopoverAnchor and a GlassPageControl given a
    PageController create their animation controller in initState. Never
    opened, or never animated, they created it for the first time in
    dispose, which Flutter tolerates only from 3.47.
  • A GlassMenuController given to a GlassMenuAnchor or a
    GlassPopoverAnchor in a rebuild drives it. The anchor kept the
    controller it was built with for life, so the new one's open and
    close did nothing; taken away, the anchor's own takes over, and an open
    panel stays open.
  • A GlassPageControl whose PageController is taken away shows
    currentPage. Its own position stood still while the controller led, so
    a currentPage moved meanwhile was not shown, nor told to a screen
    reader, until it moved again.
  • The capture walk keeps its count of open opacities when a painter throws
    inside pushOpacity, as it does in pushLayer. The count stayed raised,
    and every colour filter later in the pass was sent to the stock capture.
  • GlassBadge, GlassScaffold and GlassTabBar build without an ambient
    Directionality (#8).
  • The site's service worker precaches the shaders with the code that writes
    their uniforms: a returning browser ran the cached code of its last visit
    against the new shaders, and a press drew the glass black (#8).

https://pub.dev/packages/g1455/versions/0.3.0

v0.2.0

Choose a tag to compare

@PlugFox PlugFox released this 10 Oct 01:51
c6c1582

A minor version rather than a patch: new components, new parameters on the
sheet, the tab bar and the controls, and a declared backdrop. Existing code
builds unchanged; the one layout change is in the ripple shader's uniforms
(see Performance). Three defaults behave differently: a pressed GlassButton
swells (GlassPress.none turns it off), a sheet stands on the keyboard
instead of under it, and a switch, slider or segmented control inside a
horizontal scrollable takes a horizontal drag from the page.

A backdrop the application declares:

  • GlassBackdrop declares what is behind the glass in a subtree, and that
    glass samples a texture made from the declaration instead of a capture of
    the screen: GlassBackdrop.color, .image (an ImageProvider, placed by
    fit and alignment), .texture (a ui.Image the application holds) and
    .gradient and .painter (any GlassProxyPainter). The texture is
    made once per finish blur and size, through the same shader and the same
    blur arithmetic as a captured slot, and the host leaves those surfaces out
    of its capture: a screen whose glass is all declared takes no snapshot.
    Over a declared colour the glass draws what it draws over a captured screen
    of that colour, to 2 code values.
  • Scoped to the subtree, innermost first: GlassBackdrop.live hands a part
    back to the capture, so a page can declare its wallpaper and still let the
    bar over its list refract the list. The widget paints what it declares under
    its child unless paintBackdrop: false, and an image that has not loaded
    leaves the glass on the capture until it has; so does a replaced image,
    with the old one still painted until the new one arrives.
  • Glass over a declared backdrop is ordinary content for the capture of other
    glass, as a cheap surface is: a captured bar over declared cards shows them.
    Fused groups sample the declaration too, and a fused member is left out of
    the capture exactly when its group is, whatever GlassBackdrop sits between
    them. A draw holds the texture it was recorded with, so glass that moves
    without repainting keeps drawing while other glass under the same
    declaration asks for new blurs, and no texture is made while a frame is
    composited. Adaptive glass does not read a
    declared backdrop yet, and falls back to its declarations.
  • RenderGlassSurface.paintsWithDeclaredBackdrop,
    RenderGlassGroup.paintsWithDeclaredBackdrop,
    RenderGlassSurface.readsDeclaredBackdrop and
    GlassBackdropDeclaration.renders say what was sampled and how many
    textures were made; GlassSurfaceRecord.declared and readsCapture say
    what the ledger priced.

Components:

  • GlassStepper: a minus and a plus in one glass capsule, as UIStepper. A
    press steps at once; held, a half repeats after
    kGlassStepperRepeatDelay (500 ms) every kGlassStepperRepeatInterval
    (100 ms), and the half that would pass a limit is disabled unless wraps.
    One surface: a press, the repeat and a glyph dimming are drawn inside the
    glass and are no capture. A screen reader hears one adjustable control.
    Under right to left it is mirrored, with the minus at the end.
  • GlassPageControl: page dots on a glass capsule, as UIPageControl on its
    platter. It follows a PageController without a build and turns it; a tap
    moves one page toward the side tapped, as iOS's does, and a drag scrubs.
    One surface. A PageView beside it is retaken while it scrolls (43 captures
    a swipe in the package's tests), which belongs to the view: the same 43 with
    dots that stay put. Under right to left the first dot is at the right, and
    a count that shrinks below the current page clamps it.
  • GlassSearchBar: the glass search field with a clear button while there is
    text, and a Cancel that slides in while it has the focus. One surface, the
    field's; typing and the clear button are no capture. Cancel's slide narrows
    the glass, and that is a capture a frame for cancelDuration (16 over
    250 ms at 60 Hz, each way), one frame under reduced motion.
  • GlassBadge: a count, a label or a dot on the corner of an icon or a tab,
    as an opaque systemRed capsule. Not glass, as iOS 26's badge is not; it
    costs no surface, and a count changing on glass is a repaint inside the
    glass.
  • Their sizes (kGlassStepperSize, kGlassPageDot, kGlassPageDotGap,
    kGlassPageControlHeight, kGlassBadgeHeight, kGlassBadgeDot) are
    layout, not Apple measurements: none of the four was among the controls
    measured. The stepper, the page control and the search bar are laid
    out at least kGlassMinTapTarget tall.

iOS 26 behaviours:

  • Sheet detents: showGlassSheet(detents:, initialDetent:, onDetentChanged:) with GlassSheetDetent.medium (the content's height, as
    before; Apple's is half the window) and GlassSheetDetent.large (the whole
    height below the top safe area less 8, edge to edge). A drag between them
    follows the finger and settles at the nearer, or where a flick was heading;
    a tap on a settling sheet, or on a button in it, leaves the settle alone.
  • A large sheet is largeFinish, by default the finish with its tint at full
    alpha. Once it is all the way up it is drawn on GlassTier.cheap and reads
    no backdrop: headless, with a glass bar under it, one surface of two
    captured instead of two, and 20,608 px² of capture against 337,408. The
    other way: the sheet is then content to the capture of the glass around it,
    so a change inside it is a retake (1 against 0). A translucent
    largeFinish keeps it glass.
  • GlassTabBar(minimizeBehavior: GlassTabBarMinimizeBehavior.onScrollDown)
    collapses the bar to a circle of the selected tab's icon when the content
    scrolls down, and expands it on a scroll up or a tap on the circle, which
    selects nothing. The scroll is read by a GlassTabBarMinimizer above both
    the scroll view and the bar; GlassScaffold is one. The collapse moves
    inside a declared travel region: no record over 60 frames, against 60
    without the declaration. Collapsed, a screen reader hears one button named
    for the selected tab. GlassTabBarMinimizer.notificationPredicate (and
    GlassScaffold.minimizeNotificationPredicate) lets a list one level down,
    in a PageView, TabBarView or NestedScrollView, collapse it, and the
    direction is read on the screen, so a reverse: true list collapses it as
    any list does.
  • GlassTabBar.bottomAccessory: a glass capsule kGlassTabAccessoryHeight
    (48) tall above the bar, which moves down beside the collapsed circle. The
    bar's box keeps its height, so the body is not laid out again for it.
  • The gap above a large sheet, its radius, the circle's size, the accessory's
    height and gaps, kGlassTabMinimizeScroll (12) and the springs are layout
    taste: none was measured on Apple's devices.

Interaction:

  • GlassPress: a pressed GlassButton swells by 12 px on its longest side
    and leans up to 3 px toward a dragging finger, then springs back. Declared
    on GlassHost.press, GlassThemeData.press or GlassButton.press;
    GlassPress.none turns it off, and so do reduced motion and a disabled
    button. A feel rather than a measurement: Apple's press was not measured,
    and 17 px, another package's number, would be 1.39× a 44 px button. It
    costs two captures a press (touch-down and settle) and nothing at rest,
    because the region it grows in is declared only while it moves; declared
    always, it doubled a 44 px button's captured area (3,600 to 7,056 px²).
    Toolbar cells take the focus but do not swell: the group is one surface.
  • GlassSlider.divisions: every input, a drag, a tap, a key or a screen
    reader, lands on a stop, and onChanged is called only when the stop
    changes.
  • The switch, the slider and the segmented control drag from touch-down.
    Inside a horizontal scrollable such as a PageView a horizontal drag that
    starts on them moves the control and not the page, and a vertical swipe
    still scrolls the vertical list around them, a carousel row's included.
  • The keyboard: the button, switch, slider, segmented control, stepper, page
    control and toolbar cells take the focus (focusNode, autofocus); Space
    and Enter activate, and the arrows step the slider, the segmented control,
    the stepper and the page control. The ring
    (kGlassFocusRingColor, kGlassFocusRingWidth, kGlassFocusRingGap) is
    drawn inside the glass's own subtree, which no capture sees; a switch's or a
    segmented control's ring under other glass costs one capture as the focus
    changes.
  • Right to left: the switch is on at the left, the slider fills from the
    right, the segmented control's first segment is at the right, the toolbar's
    first item is at the right, the stepper's minus and the page control's first
    dot are at the right, and the arrow keys follow.
    SliderGeometry.fillEnd takes a textDirection.
  • GlassConcentric: Apple's concentric corners as arithmetic, a shape's
    radius as its container's less the inset, with a floor. The segmented
    control's capsule (pixel for pixel what it was) and the focus rings use it.

Fixes:

  • A dismissible sheet follows the finger: a 116 px drag moved it about
    4.5 px. It closes past a third of its height or on a flick, and while it
    moves it declares where it travels: one retake a drag instead of 16.
  • Under right to left, the segmented control's capsule sat under the wrong
    label, and GlassButtonGroup brightened the wrong cell.
  • A sheet stands on the keyboard, as an iOS sheet does: 8 px above it at
    medium, on it at large. Its content sees no bottom viewInsets, so content
    already padded by them is not padded twice.
  • A sheet popped while a finger drags it no longer pops the page under it when
    the finger lifts, and a drag down from large after the content or the window
    changed no longer jumps.
  • GlassSlider sends one onChangeStart before its changes and one
    onChangeEnd after, carrying the last value it...
Read more

v0.1.4

Choose a tag to compare

@PlugFox PlugFox released this 05 Oct 13:10
33eb89e
  • showGlassSheet(barrierDismissible: false) is a sheet only its content
    closes, as UIKit's isModalInPresentation: a drag or a flick used to close
    it whatever the flag said. Now a pull gives a quarter of the way, no more
    than half the sheet's height, and springs back; the dim and Escape were
    already held by the flag.

Documentation:

  • The API reference is grouped into topics, each with a page of its own:
    Getting started, Foundations, Panels and controls, Modals, Composition,
    Capture control, Cost and policy, Diagnostics. Every public name has a doc
    comment, the main widgets an example and a list of what to see next with
    their page on the site, and dart doc reports no unresolved reference.
  • GlassProxy, the way to tell the capture what a subtree is, is in the
    README at last: a stand-in for a video, a map or a platform view (which
    record nothing, so the glass over them showed a hole), a subtree left out,
    an opaque cover, a blur the shadow filter keeps. The site has a page for it,
    live: Capture control.
  • Every measured number now says where it comes from: the devices, their
    systems, the renderer, the metric, the dates, and the Flutter and engine
    revisions, in the README and on the site's How it works.
  • An agent skill for Claude Code, Codex, Cursor, Antigravity, Gemini CLI and
    Copilot, in skills/g1455/: the rules for writing glass that works, with
    every page of the site as a reference. npx skills add PlugFox/g1455, or
    /plugin marketplace add PlugFox/g1455 in Claude Code. The site publishes
    it at /.well-known/agent-skills/ (npx skills add https://g1455.plugfox.dev), with llms.txt, llms-full.txt and every
    page as markdown at its address plus .md.
  • pub.dev shows a square, still picture of the package: its icon leads the
    screenshots, ahead of the loops, which it showed as one frame. pub.dev takes
    ten, so the materialize loop makes room; the alert's shows the same arrival.

https://pub.dev/packages/g1455/versions/0.1.4

v0.1.3

Choose a tag to compare

@github-actions github-actions released this 04 Oct 13:34
  • The README's images show on pub.dev again. 0.1.2 linked the banner, the
    showcase loops and the How it works cards by relative path, and pub.dev
    drops a relative image; they are linked from GitHub again, as in 0.1.1.

https://pub.dev/packages/g1455/versions/0.1.3

v0.1.2

Choose a tag to compare

@PlugFox PlugFox released this 04 Oct 13:20
ff976af

Glass that reads its backdrop:

  • GlassHost(adaptive: GlassAdaptive()) tells each GlassBar, GlassCard
    and GlassButton the mean level of the captured backdrop inside its own
    box, and the glass picks the branch of .regular and its label from it: a
    bar over a photograph's sky and a button over its shadow each wear the
    branch Apple's material would. Off by default and free when off. On, a
    frame that captures reads back a 4 × 4-pixel cell per surface,
    asynchronously, at most once per interval (250 ms, a second on the web,
    where CanvasKit reads back synchronously), and a frame that keeps its
    capture reads nothing. A band of 12 code values and a hold of 600 ms keep a
    glass near the threshold from flickering; crossings tween over 300 ms and
    cut under reduced motion. A finish a component, the host or a theme names
    is kept, and only its label follows; under richBackdrop the label stays
    chosen against every backdrop. GlassTheme.of(context).reading hands the
    verdict to content on the glass. A raw GlassSurface, the scroll edge, the
    tab bar and the segmented control do not adapt yet.

Motion:

  • GlassMorph: swap the child and the glass flows to its size, the way a
    button becomes its menu. It measures the child; width and height pin an
    axis, and alignment names the point that holds still. While it grows it
    is a two-shape union, a body on a spring and a capsule running ahead, so
    the outline has a neck. At rest it is one plain surface, and a settled
    morph is that surface pixel for pixel. GlassMorphMotion.fluid and
    .calm; under reduced motion it changes at once. A morph is a capture per
    frame, unless a GlassTravel around a fixed-size ancestor holds it.
  • The held drop in the switch, the slider, the segmented control and the tab
    bar stretches as it sets off and squashes as it stops, from its
    acceleration, and springs back round; at rest and at a constant speed it
    keeps its shape. GlassHost.dropMotion or GlassThemeData.dropMotion sets
    it for the app, and each control's dropMotion overrides it;
    GlassDropMotion.none turns it off, and so does reduced motion. It costs no
    capture: the drop deforms inside the region it already travels in. It does
    repaint the drop's own layer on the frames it deforms.
  • GlassTabItem takes an iconBuilder and a labelBuilder, handed the
    colour the bar draws the item in, so an SVG, an image or a badge matches its
    neighbours. Breaking for code that reads it: GlassTabItem.icon is now
    nullable, so item.icon used as an IconData needs a null check.

Start-up and scaffolding:

  • GlassHost.precache() compiles the package's shaders before the first
    frame. Call it in main() after WidgetsFlutterBinding.ensureInitialized(),
    and the first frame that has a capture is drawn through the optics rather
    than as the plain blurred backdrop. It shares the loads a host starts on its
    own, so it never compiles a program twice; group: false and
    ripple: false leave those out.
  • GlassScaffold is a screen wired the way the README recommends: a host when
    none is above it, a top bar in a soft GlassScrollEdge, an optional bottom
    bar and floating action, and a body that scrolls under the bars and is told
    their extents through MediaQuery.padding.

Fixed:

  • A shader that fails to load no longer escapes as an uncaught error. A host
    or a ripple that waited on it reports it through FlutterError, and the
    failed load is forgotten, so the next host or GlassHost.precache() tries
    again rather than being handed the same failure.
  • On the web, a painter that throws while a glass layer records no longer
    leaks the shaders it drew with before the throw.

The package page and the README:

  • The package page links the site as its documentation, names what is in the
    box in its description, and adds glass to its topics.
  • The README opens with a loop of a whole app screen, shot headless like the
    tiles by tool/showcase.sh screen. How it works is four cards. The quick
    start is a whole app that runs as pasted. New sections give the values of
    every GlassFinish preset, five common patterns, and what is not here and
    why: no dispersion, because Apple's material has none to measure (D103),
    and one shape, RSuperellipse. A test keeps the README's code identical to
    files that are analyzed and pumped, and its finish table equal to the
    constants. Every image is linked relatively.

https://pub.dev/packages/g1455/versions/0.1.2

v0.1.1

Choose a tag to compare

@PlugFox PlugFox released this 04 Oct 00:42
b7200d2

What a screen reader is told and can do:

  • GlassSwitch and GlassSlider take a semanticLabel. A screen reader steps
    GlassSlider with increase and decrease, by semanticStep (a tenth by
    default), through onChangeStart, onChanged and onChangeEnd, and never
    past either end.
  • GlassButton takes a semanticLabel that is read in place of its child,
    for a button that is only an icon.
  • showGlassSheet takes a barrierLabel, as showGlassDialog already did.
    GlassMenuAnchor and GlassPopoverAnchor take one too. Their barrier used
    to be a tap target with no label, so a screen reader had no way to close a
    popover.

On a page:

  • showGlassSheet takes constraints, as showModalBottomSheet does. A
    maxWidth keeps the sheet at its content's width, centred, on a window
    wider than that. Null, it spans the window as before.
  • The labels of GlassButton, GlassSegmentedControl and GlassTabBar are
    not text to select. Under a SelectionArea, a drag across the page used to
    take them along with the prose around them.

Fixed:

  • Glass draws on CanvasKit, which is every Safari and every browser without
    WasmGC. CanvasKit keeps a fragment shader's uniforms by reference until the
    picture is rasterized, and the glass released its shaders straight after
    the draw. So it drew from freed memory: a shape in the wrong place, a solid
    grey or green slab, a different one each frame. On the web a shader now
    lives as long as the picture drawn with it.
  • Glass standing on glass no longer moves what is under the glass it stands
    on. When a level was captured, the walk painted the levels below it, and a
    CompositedTransformTarget among them wrote the walk's offset into its own
    live LeaderLayer. A SelectionArea creates one. So with a popover open
    over a bar, the bar's text jumped right and down, and it stayed there until
    the bar repainted. The walk now puts back every live offset it touches.
  • A GlassGroup drawn in tiles no longer loses members on the web. Skwasm's
    fragment shader keeps its uniforms by reference rather than copying them on
    each draw, so every tile drew the last tile's members, and blobs flickered
    in and out. On the web each tile now gets its own shader.
  • A platform view or a Texture is a hole in the capture of an upper level,
    as it already was in the base level. It used to be a grey stub. On the web
    every SelectionArea lays a transparent platform view under what it holds,
    so a bar over a selectable page showed grey.

Documented:

  • The README says what the glass costs on the web outside Chromium — Safari,
    Firefox and every browser on iOS, Chrome for iOS included, get CanvasKit,
    where each capture is a synchronous GPU readback — and what to do about it:
    allow Skwasm on WebKit, or declare a cheaper rung where CanvasKit remains.

https://pub.dev/packages/g1455/versions/0.1.1