Skip to main content

liquidglass_light 0.0.2 copy "liquidglass_light: ^0.0.2" to clipboard
liquidglass_light: ^0.0.2 copied to clipboard

A high-performance Flutter package that creates liquid, glass-like borders with gyro-simulated light reflections and dynamic gradients.

example/lib/main.dart

import 'dart:math' as math;
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

import 'package:liquidglass_light/liquid_border/liquid_border.dart';

void main() {
  WidgetsFlutterBinding.ensureInitialized();
  SystemChrome.setPreferredOrientations([
    DeviceOrientation.portraitUp,
    DeviceOrientation.portraitDown,
  ]);
  runApp(const LiquidBorderApp());
}

class LiquidBorderApp extends StatefulWidget {
  const LiquidBorderApp({super.key});

  @override
  State<LiquidBorderApp> createState() => _LiquidBorderAppState();
}

class _LiquidBorderAppState extends State<LiquidBorderApp> {
  // Global state for the gyro feature
  bool isGyroEnabled = true;

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Liquid Border Lab',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        brightness: Brightness.dark,
        scaffoldBackgroundColor: const Color(0xFF0F0F0F),
        useMaterial3: true,
      ),
      home: LiquidShowcaseScreen(
        isGyroEnabled: isGyroEnabled,
        onToggleGyro: (value) => setState(() => isGyroEnabled = value),
      ),
    );
  }
}

class LiquidShowcaseScreen extends StatelessWidget {
  final bool isGyroEnabled;
  final ValueChanged<bool> onToggleGyro;

  const LiquidShowcaseScreen({
    super.key,
    required this.isGyroEnabled,
    required this.onToggleGyro,
  });

  @override
  Widget build(BuildContext context) {
    return LiquidGyro(
      enableGyro: isGyroEnabled,
      fallbackAlignment: Alignment.topLeft,
      child: _ShowcaseContent(
        isGyroEnabled: isGyroEnabled,
        onToggleGyro: onToggleGyro,
      ),
    );
  }
}

class _ShowcaseContent extends StatefulWidget {
  final bool isGyroEnabled;
  final ValueChanged<bool> onToggleGyro;

  const _ShowcaseContent({
    required this.isGyroEnabled,
    required this.onToggleGyro,
  });

  @override
  State<_ShowcaseContent> createState() => _ShowcaseContentState();
}

class _ShowcaseContentState extends State<_ShowcaseContent>
    with SingleTickerProviderStateMixin {
  late AnimationController _bgController;

  @override
  void initState() {
    super.initState();
    _bgController = AnimationController(
      vsync: this,
      duration: const Duration(seconds: 12),
    )..repeat(reverse: true);
  }

  @override
  void dispose() {
    _bgController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    const baseStyle = LiquidStyle.standard;

    return Scaffold(
      body: Stack(
        children: [
          const _BackgroundGradient(),
          _buildAnimatedBlobs(),
          CustomScrollView(
            slivers: [
              _buildAppBar(),
              SliverPadding(
                padding: const EdgeInsets.all(24),
                sliver: SliverList(
                  delegate: SliverChildListDelegate([
                    _sectionHeader("1. Gyro Controls"),
                    const SizedBox(height: 8),
                    Text(
                      widget.isGyroEnabled
                          ? "Tilt your phone to move the light source."
                          : "Gyro disabled. Using static Top-Left light.",
                      style:
                          const TextStyle(color: Colors.white30, fontSize: 12),
                    ),
                    const SizedBox(height: 24),
                    _buildPrimitivesRow(baseStyle),
                    const SizedBox(height: 48),
                    _sectionHeader("2. Intensity Presets"),
                    const SizedBox(height: 16),
                    _buildPresetsRow(),
                    const SizedBox(height: 48),
                    _sectionHeader("3. Real-World Glass Card"),
                    const SizedBox(height: 16),
                    _buildCreditCard(baseStyle),
                    const SizedBox(height: 48),
                    _sectionHeader("4. High Performance List"),
                    const SizedBox(height: 16),
                    _buildScrollingList(baseStyle),
                    const SizedBox(height: 100),
                  ]),
                ),
              ),
            ],
          ),
        ],
      ),
      floatingActionButton: _buildDebugFab(context),
    );
  }

  Widget _buildAppBar() {
    return SliverAppBar(
      expandedHeight: 120,
      backgroundColor: Colors.transparent,
      floating: false,
      pinned: true,
      flexibleSpace: FlexibleSpaceBar(
        titlePadding: const EdgeInsets.only(left: 24, bottom: 16),
        title: const Text(
          "Liquid Borders",
          style: TextStyle(fontWeight: FontWeight.bold, letterSpacing: -0.5),
        ),
        background: Container(
          decoration: const BoxDecoration(
            gradient: LinearGradient(
              begin: Alignment.topCenter,
              end: Alignment.bottomCenter,
              colors: [Colors.black54, Colors.transparent],
            ),
          ),
        ),
      ),
      actions: [
        Row(
          children: [
            const Icon(Icons.screen_rotation, size: 16, color: Colors.white70),
            const SizedBox(width: 8),
            Switch(
              value: widget.isGyroEnabled,
              onChanged: widget.onToggleGyro,
              activeColor: Colors.cyanAccent,
            ),
            const SizedBox(width: 16),
          ],
        )
      ],
    );
  }

  Widget _buildDebugFab(BuildContext context) {
    final controller = LiquidGyro.of(context);
    if (controller == null) return const SizedBox();

    return ValueListenableBuilder<Alignment>(
      valueListenable: controller,
      builder: (context, alignment, child) {
        return FloatingActionButton.extended(
          onPressed: () {
            widget.onToggleGyro(!widget.isGyroEnabled);
          },
          icon: Icon(widget.isGyroEnabled ? Icons.sensors : Icons.sensors_off),
          label: Text(
            widget.isGyroEnabled
                ? "Light: ${_formatAlignment(alignment)}"
                : "Gyro Off",
            style: const TextStyle(fontFamily: 'monospace', fontSize: 12),
          ),
          backgroundColor: Colors.white.withValues(alpha: 0.9),
          foregroundColor: Colors.black,
        );
      },
    );
  }

  Widget _buildAnimatedBlobs() {
    return AnimatedBuilder(
        animation: _bgController,
        builder: (context, child) {
          return Stack(
            children: [
              _FloatingBlob(
                color: Colors.blueAccent.withValues(alpha: 0.3),
                size: 300,
                x: -0.5 + (math.sin(_bgController.value * math.pi) * 0.2),
                y: -0.5,
              ),
              _FloatingBlob(
                color: Colors.purpleAccent.withValues(alpha: 0.2),
                size: 250,
                x: 0.8,
                y: 0.2 + (math.cos(_bgController.value * math.pi) * 0.2),
              ),
              _FloatingBlob(
                color: Colors.cyanAccent.withValues(alpha: 0.15),
                size: 200,
                x: -0.2,
                y: 0.8,
              ),
            ],
          );
        });
  }

  Widget _buildPrimitivesRow(LiquidStyle style) {
    return Wrap(
      spacing: 24,
      runSpacing: 24,
      alignment: WrapAlignment.center,
      children: [
        _ShowcaseItem(
          label: "Circle",
          child: LiquidContainer(
            shape: LiquidShape.circle,
            width: 80,
            height: 80,
            enableGyro: widget.isGyroEnabled,
            backgroundColor: Colors.white.withValues(alpha: 0.1),
            style: style,
            child: const Center(
                child: Icon(Icons.water_drop, size: 24, color: Colors.white70)),
          ),
        ),
        _ShowcaseItem(
          label: "Stadium",
          child: LiquidContainer(
            shape: LiquidShape.stadium,
            width: 100,
            height: 50,
            enableGyro: widget.isGyroEnabled,
            backgroundColor: Colors.white.withValues(alpha: 0.1),
            style: style,
            child: const Center(
                child: Text("Action", style: TextStyle(fontSize: 12))),
          ),
        ),
        _ShowcaseItem(
          label: "Rectangle",
          child: LiquidContainer(
            shape: LiquidShape.rectangle,
            width: 80,
            height: 80,
            enableGyro: widget.isGyroEnabled,
            borderRadius: BorderRadius.circular(20),
            backgroundColor: Colors.white.withValues(alpha: 0.1),
            style: style,
            child: const Center(
                child: Icon(Icons.grid_view, size: 20, color: Colors.white70)),
          ),
        ),
      ],
    );
  }

  Widget _buildPresetsRow() {
    Widget presetBox(String name, LiquidStyle s) {
      return Column(
        children: [
          LiquidContainer(
            width: 70,
            height: 70,
            borderRadius: BorderRadius.circular(16),
            backgroundColor: Colors.white.withValues(alpha: 0.05),
            enableGyro: widget.isGyroEnabled,
            style: s,
            child: const SizedBox(),
          ),
          const SizedBox(height: 12),
          Text(name,
              style: const TextStyle(fontSize: 11, color: Colors.white54)),
        ],
      );
    }

    return Row(
      mainAxisAlignment: MainAxisAlignment.spaceAround,
      children: [
        presetBox("Soft", LiquidStyle.soft),
        presetBox("Standard", LiquidStyle.standard),
        presetBox("Crisp", LiquidStyle.crisp),
      ],
    );
  }

  Widget _buildCreditCard(LiquidStyle style) {
    final cardStyle = style.copyWith(
      intensity: 0.9,
      insideGlowIntensity: 0.3,
      refractionIntensity: 0.8,
      withInnerHighlight: true,
      blurStrength: 8.0,
    );

    return LiquidContainer(
      width: double.infinity,
      height: 220,
      shape: LiquidShape.rectangle,
      borderRadius: BorderRadius.circular(24),
      backgroundColor: Colors.white.withValues(alpha: 0.08),
      enableGyro: widget.isGyroEnabled,
      style: cardStyle,
      child: Stack(
        children: [
          Positioned.fill(
            child: DecoratedBox(
              decoration: BoxDecoration(
                borderRadius: BorderRadius.circular(20),
                gradient: LinearGradient(
                  begin: Alignment.topLeft,
                  end: Alignment.bottomRight,
                  colors: [
                    Colors.white.withValues(alpha: 0.1),
                    Colors.transparent,
                    Colors.white.withValues(alpha: 0.05),
                  ],
                ),
              ),
            ),
          ),
          Padding(
            padding: const EdgeInsets.all(24.0),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children: [
                Row(
                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
                  children: [
                    const Icon(Icons.blur_on, color: Colors.white70, size: 32),
                    Text("LIQUID GYRO",
                        style: TextStyle(
                            color: Colors.white.withValues(alpha: 0.8),
                            fontWeight: FontWeight.bold,
                            letterSpacing: 1.5)),
                  ],
                ),
                Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    const Text("4920  ••••  ••••  9012",
                        style: TextStyle(
                            fontSize: 24,
                            fontFamily: 'Courier',
                            letterSpacing: 3,
                            fontWeight: FontWeight.w500)),
                    const SizedBox(height: 20),
                    Row(
                      children: [
                        // FIX: const added
                        const Column(
                          crossAxisAlignment: CrossAxisAlignment.start,
                          children: [
                            Text("VALID THRU",
                                style: TextStyle(
                                    fontSize: 9, color: Colors.white60)),
                            Text("12/28",
                                style: TextStyle(
                                    fontSize: 14, fontWeight: FontWeight.bold)),
                          ],
                        ),
                        const SizedBox(width: 32),
                        // FIX: const added
                        const Column(
                          crossAxisAlignment: CrossAxisAlignment.start,
                          children: [
                            Text("CARDHOLDER",
                                style: TextStyle(
                                    fontSize: 9, color: Colors.white60)),
                            Text("FLUTTER DEV",
                                style: TextStyle(
                                    fontSize: 14, fontWeight: FontWeight.bold)),
                          ],
                        ),
                      ],
                    )
                  ],
                )
              ],
            ),
          ),
        ],
      ),
    );
  }

  Widget _buildScrollingList(LiquidStyle style) {
    return SizedBox(
      height: 140,
      child: ListView.builder(
        scrollDirection: Axis.horizontal,
        physics: const BouncingScrollPhysics(),
        itemCount: 8,
        itemBuilder: (context, index) {
          return Padding(
            padding: const EdgeInsets.only(right: 16),
            child: LiquidContainer(
              width: 100,
              shape: LiquidShape.stadium,
              style: style.copyWith(thickness: 1.2),
              backgroundColor: Colors.white.withValues(alpha: 0.05),
              enableGyro: false,
              child: Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  Icon(
                    index % 2 == 0
                        ? Icons.water_drop_outlined
                        : Icons.auto_awesome,
                    color: Colors.white70,
                  ),
                  const SizedBox(height: 8),
                  Text("Item ${index + 1}",
                      style: const TextStyle(fontSize: 12)),
                ],
              ),
            ),
          );
        },
      ),
    );
  }

  Widget _sectionHeader(String title) {
    return Text(
      title.toUpperCase(),
      style: const TextStyle(
        color: Colors.white38,
        fontSize: 12,
        fontWeight: FontWeight.bold,
        letterSpacing: 1.5,
      ),
    );
  }

  String _formatAlignment(Alignment a) {
    return "X:${a.x.toStringAsFixed(1)}, Y:${a.y.toStringAsFixed(1)}";
  }
}

class _ShowcaseItem extends StatelessWidget {
  final String label;
  final Widget child;
  const _ShowcaseItem({required this.label, required this.child});

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        child,
        const SizedBox(height: 12),
        Text(label,
            style: const TextStyle(fontSize: 12, color: Colors.white54)),
      ],
    );
  }
}

class _BackgroundGradient extends StatelessWidget {
  const _BackgroundGradient();

  @override
  Widget build(BuildContext context) {
    return Container(
      decoration: const BoxDecoration(
        gradient: LinearGradient(
          begin: Alignment.topLeft,
          end: Alignment.bottomRight,
          colors: [
            Color(0xFF0F2027),
            Color(0xFF203A43),
            Color(0xFF2C5364),
          ],
        ),
      ),
    );
  }
}

class _FloatingBlob extends StatelessWidget {
  final Color color;
  final double size;
  final double x;
  final double y;

  const _FloatingBlob({
    required this.color,
    required this.size,
    required this.x,
    required this.y,
  });

  @override
  Widget build(BuildContext context) {
    return Align(
      alignment: Alignment(x, y),
      child: Container(
        width: size,
        height: size,
        decoration: BoxDecoration(
          shape: BoxShape.circle,
          gradient: RadialGradient(
            colors: [color, Colors.transparent],
            stops: const [0.0, 0.7],
          ),
        ),
      ),
    );
  }
}

extension LiquidStyleDemoExt on LiquidStyle {
  LiquidStyle copyWith({
    double? thickness,
    double? intensity,
    double? insideGlowIntensity,
    double? refractionIntensity,
    Alignment? lightSource,
    bool? withInnerHighlight,
    bool? withOuterGlow,
    Color? baseColor,
    double? blurStrength,
  }) {
    return LiquidStyle(
      thickness: thickness ?? this.thickness,
      intensity: intensity ?? this.intensity,
      insideGlowIntensity: insideGlowIntensity ?? this.insideGlowIntensity,
      refractionIntensity: refractionIntensity ?? this.refractionIntensity,
      lightSource: lightSource ?? this.lightSource,
      withInnerHighlight: withInnerHighlight ?? this.withInnerHighlight,
      withOuterGlow: withOuterGlow ?? this.withOuterGlow,
      baseColor: baseColor ?? this.baseColor,
      blurStrength: blurStrength ?? this.blurStrength,
    );
  }
}
2
likes
125
points
13
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

A high-performance Flutter package that creates liquid, glass-like borders with gyro-simulated light reflections and dynamic gradients.

Repository (GitHub)
View/report issues

License

MIT (license)

Dependencies

flutter, sensors_plus

More

Packages that depend on liquidglass_light