Installation
Add the package, put one GlassHost above the navigator, and lay the first glass over content.
GlassHost · GlassBar · kTextContrastAA · Source
g1455 draws Liquid Glass in Flutter: refraction, blur, tint and a rim over whatever is painted behind it. Getting it on
screen takes three steps: add the package, put one GlassHost at the top of the app, and place glass over some content.
The package is pure Dart and shaders. It ships no platform code, so there is nothing to configure in Xcode or Gradle.
Install
flutter pub add g1455
It needs Flutter 3.47 or later. Then import it wherever you use glass:
import 'package:g1455/g1455.dart';
Add the host
Every piece of glass needs a GlassHost above it. The host records what is painted under all the glass of the screen into one shared image, and every surface samples its own slice of it. Without a host, a glass surface draws no glass at all, only its child.
Put the host in the builder: of your MaterialApp (or WidgetsApp, CupertinoApp). That places it above the
navigator, which is also where dialogs, sheets, menus and popovers are built, so they find the host too:
MaterialApp(
builder: (BuildContext context, Widget? child) => GlassHost(child: child!),
home: const HomePage(),
)
One host per app is the normal setup. Don't nest a host around each widget.
The first glass
Glass shows what is behind it, so it needs something behind it: lay it over content in a Stack.
GlassBar is a floating capsule for navigation that also picks a legible label colour for its
children:
Stack(
children: <Widget>[
Positioned.fill(child: content), // what the glass refracts
const Positioned(
top: 16,
left: 16,
right: 16,
child: GlassBar(child: Text('Library')),
),
],
)
The "Code" tab has a complete main.dart with a scrolling list under a bar.
Tip
Tell the host what is behind the glass. For a list of photos or a feed, pass richBackdrop: true and
minLabelContrast: kTextContrastAA; over a flat colour, pass backdrop:. Labels are then chosen and kept readable
for that case. See Legibility & theme.
The first frame has no glass
The host captures the backdrop after a frame has been painted, and the glass draws that capture on the next frame. So the very first frame of a screen shows the glass's children without the glass itself. That is by design and is not visible in practice, but it matters in widget tests: pump one more frame before you look for glass.
Next steps
- How it works: one capture for the whole screen, and only when something changed.
- What the app declares: the backdrop, reduce transparency, contrast, thermal state.
- GlassHost and GlassSurface: the two building blocks.
- Finishes: regular, clear and frosted glass.
- The components: Bar, Button, Card, Switch, Slider, Tab bar, Alert, Sheet and more.
- Performance: what glass costs and how to keep it cheap.
Code
import 'package:flutter/material.dart';
import 'package:g1455/g1455.dart';
void main() => runApp(const GlassApp());
class GlassApp extends StatelessWidget {
const GlassApp({super.key});
@override
Widget build(BuildContext context) => MaterialApp(
title: 'Glass',
theme: ThemeData.dark(),
// One host for the whole app, above the navigator, so pages, dialogs,
// sheets and menus are all under it.
builder: (BuildContext context, Widget? child) => GlassHost(
// A feed of colourful tiles scrolls under the glass: choose labels for
// the worst case, and keep them at WCAG AA.
richBackdrop: true,
minLabelContrast: kTextContrastAA,
backdrop: const Color(0xFF101014),
child: child!,
),
home: const LibraryPage(),
);
}
class LibraryPage extends StatelessWidget {
const LibraryPage({super.key});
@override
Widget build(BuildContext context) {
final EdgeInsets safe = MediaQuery.paddingOf(context);
return Scaffold(
backgroundColor: const Color(0xFF101014),
body: Stack(
children: <Widget>[
// The content: what the glass refracts.
ListView.builder(
padding: EdgeInsets.fromLTRB(16, safe.top + 76, 16, safe.bottom + 16),
itemCount: 30,
itemBuilder: (BuildContext context, int i) => Container(
height: 120,
margin: const EdgeInsets.only(bottom: 12),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20),
gradient: LinearGradient(
colors: <Color>[
HSVColor.fromAHSV(1, (i * 37) % 360.0, 0.7, 0.9).toColor(),
HSVColor.fromAHSV(1, (i * 37 + 60) % 360.0, 0.8, 0.5).toColor(),
],
),
),
),
),
// The glass: a bar floating over the list.
Positioned(
top: safe.top + 8,
left: 16,
right: 16,
child: const GlassBar(
child: Row(
children: <Widget>[
Icon(Icons.photo_library_outlined),
SizedBox(width: 12),
Expanded(
child: Text('Library', style: TextStyle(fontSize: 17, fontWeight: FontWeight.w600)),
),
Icon(Icons.search),
],
),
),
),
],
),
);
}
}