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

(Experimental) Progressive blur implementation in Flutter.

example/lib/main.dart

import 'dart:convert';
import 'dart:math' as math;

import 'package:example/albums.dart';
import 'package:figma_squircle/figma_squircle.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:palette_generator/palette_generator.dart';
import 'package:progressive_blur/progressive_blur.dart';

Future<void> main() async {
  await ProgressiveBlurWidget.precache();
  runApp(const App());
}

class App extends StatelessWidget {
  const App({super.key});

  @override
  Widget build(BuildContext context) {
    return CupertinoApp(
      debugShowCheckedModeBanner: false,
      theme: MaterialBasedCupertinoThemeData(
        materialTheme: ThemeData.from(
          colorScheme: ColorScheme.fromSeed(
            seedColor: Colors.blue,
            brightness: Brightness.light,
          ),
        ),
      ),
      home: const MainPage(),
    );
  }
}

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

  @override
  State<MainPage> createState() => _MainPageState();
}

class _MainPageState extends State<MainPage> {
  var _displayIndex = 0;
  final _albums = <(String, String, String, String)>[];

  @override
  void initState() {
    super.initState();
    _loadAlbums();
  }

  void _loadAlbums() {
    final json = jsonDecode(data) as List<dynamic>;
    for (final album in json) {
      final albumMap = album as Map<String, dynamic>;
      _albums.add((
        albumMap['image'] as String,
        albumMap['url'] as String,
        albumMap['name'] as String,
        albumMap['artist'] as String,
      ));
    }
  }

  @override
  Widget build(BuildContext context) {
    final child = switch (_displayIndex) {
      0 => CustomScrollView(
          slivers: [
            const CupertinoSliverNavigationBar(
              largeTitle: Text('Music'),
              border: Border(),
            ),
            SliverPadding(
              padding: const EdgeInsets.all(16.0),
              sliver: SliverLayoutBuilder(
                builder: (context, constraints) {
                  const preferredItemSize = 160.0;
                  final crossAxisCount = math.max(
                    1,
                    (constraints.crossAxisExtent / preferredItemSize).floor(),
                  );

                  return SliverGrid.builder(
                    gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
                      crossAxisCount: crossAxisCount,
                      mainAxisSpacing: 16.0,
                      crossAxisSpacing: 16.0,
                      childAspectRatio: 1.0,
                    ),
                    itemCount: _albums.length,
                    itemBuilder: (context, index) {
                      final album = _albums[index];

                      return _AlbumCard(
                        key: ValueKey(album.$1),
                        imageUrl: album.$1,
                        redirectUrl: album.$2,
                        title: album.$3,
                        artist: album.$4,
                      );
                    },
                  );
                },
              ),
            ),
          ],
        ),
      1 => FlutterMap(
          children: [
            ProgressiveBlurWidget(
              sigma: 16.0,
              linearGradientBlur: const LinearGradientBlur(
                values: [1, 0],
                stops: [0, 0.125],
                start: Alignment.topCenter,
                end: Alignment.bottomCenter,
              ),
              child: TileLayer(
                urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
                userAgentPackageName: 'com.kekland.progressive_blur_example',
              ),
            ),
          ],
        ),
      _ => throw Exception('Invalid index'),
    };

    return Scaffold(
      bottomNavigationBar: CupertinoTabBar(
        currentIndex: _displayIndex,
        onTap: (index) => setState(() => _displayIndex = index),
        iconSize: 22.0,
        items: const [
          BottomNavigationBarItem(
            icon: Icon(CupertinoIcons.music_note),
            label: 'Music',
          ),
          BottomNavigationBarItem(
            icon: Icon(CupertinoIcons.map),
            label: 'Map',
          ),
        ],
      ),
      body: child,
    );
  }
}

class _AlbumCard extends StatefulWidget {
  const _AlbumCard({
    super.key,
    required this.imageUrl,
    required this.redirectUrl,
    required this.title,
    required this.artist,
  });

  final String imageUrl;
  final String redirectUrl;
  final String title;
  final String artist;

  @override
  State<_AlbumCard> createState() => _AlbumCardState();
}

class _AlbumCardState extends State<_AlbumCard>
    with AutomaticKeepAliveClientMixin {
  Color? _prominentColor;

  @override
  void initState() {
    super.initState();
    _loadProminentColor();
  }

  Future<void> _loadProminentColor() async {
    final palette = await PaletteGenerator.fromImageProvider(
      NetworkImage(widget.imageUrl),
    );

    if (palette.dominantColor?.color != null) {
      _prominentColor = Color.lerp(
        palette.dominantColor?.color,
        Colors.black,
        0.5,
      );
      if (mounted) setState(() {});
    }
  }

  @override
  Widget build(BuildContext context) {
    super.build(context);

    final child = AspectRatio(
      aspectRatio: 1.0,
      child: Container(
        color: Colors.grey,
        child: Image.network(
          widget.imageUrl,
          fit: BoxFit.cover,
          errorBuilder: (context, _, __) => Container(color: Colors.black),
        ),
      ),
    );

    final cupertinoTheme = CupertinoTheme.of(context);

    return Stack(
      clipBehavior: Clip.none,
      children: [
        ClipSmoothRect(
          radius: SmoothBorderRadius(
            cornerRadius: 32,
            cornerSmoothing: 0.6,
          ),
          child: ProgressiveBlurWidget(
            tintColor: _prominentColor ?? Colors.black.withValues(alpha: 0.5),
            linearGradientBlur: const LinearGradientBlur(
              values: [0, 1],
              stops: [0.5, 0.8],
              start: Alignment.topCenter,
              end: Alignment.bottomCenter,
            ),
            sigma: 24.0,
            blurTextureDimensions: 128,
            child: DecoratedBox(
              position: DecorationPosition.foreground,
              decoration: const BoxDecoration(
                gradient: LinearGradient(
                  begin: Alignment.center,
                  end: Alignment.bottomCenter,
                  colors: [
                    Colors.transparent,
                    Colors.black38,
                  ],
                ),
              ),
              child: child,
            ),
          ),
        ),
        Positioned(
          left: 16.0,
          right: 16.0,
          bottom: 16.0,
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            mainAxisSize: MainAxisSize.min,
            children: [
              Text(
                widget.title,
                maxLines: 2,
                overflow: TextOverflow.ellipsis,
                style: cupertinoTheme.textTheme.navLargeTitleTextStyle.copyWith(
                  color: Colors.white,
                  fontSize: 20.0,
                ),
              ),
              Text(
                widget.artist,
                style: cupertinoTheme.textTheme.textStyle.copyWith(
                  color: Colors.white38,
                  fontSize: 14.0,
                ),
              ),
            ],
          ),
        ),
      ],
    );
  }

  @override
  bool get wantKeepAlive => true;
}
33
likes
160
points
189
downloads

Publisher

verified publisherkekland.com

Weekly Downloads

(Experimental) Progressive blur implementation in Flutter.

Repository (GitHub)

Documentation

API reference

License

MIT (license)

Dependencies

flutter, flutter_shaders

More

Packages that depend on progressive_blur