material3_expressive_loading_indicator 0.1.0 copy "material3_expressive_loading_indicator: ^0.1.0" to clipboard
material3_expressive_loading_indicator: ^0.1.0 copied to clipboard

Material Design 3 expressive loading and progress indicators for Flutter: morphing circular indicator, outlined variant, and wavy linear progress.

example/lib/main.dart

import 'package:material3_expressive_loading_indicator/material3_expressive_loading_indicator.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:material_new_shapes/material_new_shapes.dart';

import 'demo_providers.dart';

void main() {
  runApp(const ProviderScope(child: MyApp()));
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Expressive Loading Indicator Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const MyHomePage(),
    );
  }
}

class MyHomePage extends ConsumerWidget {
  const MyHomePage({super.key});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final linearProgress = ref.watch(linearDemoProgressProvider);
    final linearAmplitude = ref.watch(linearDemoAmplitudeProvider);

    return Scaffold(
      appBar: AppBar(
        title: const Text('Expressive Loading Indicator Demo'),
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
      ),
      body: Center(
        child: SingleChildScrollView(
          padding: const EdgeInsets.symmetric(vertical: 24),
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              const Text(
                'Default (filled) loading indicator:',
                style: TextStyle(fontSize: 16),
              ),
              const SizedBox(height: 16),
              const ExpressiveLoadingIndicator(),

              const SizedBox(height: 32),
              const Text(
                'Outlined loading indicator:',
                style: TextStyle(fontSize: 16),
              ),
              const SizedBox(height: 16),
              const ExpressiveLoadingIndicator(
                style: ExpressiveLoadingIndicatorStyle.outlined,
              ),

              const SizedBox(height: 32),
              const Text(
                'Outlined, custom color & stroke:',
                style: TextStyle(fontSize: 16),
              ),
              const SizedBox(height: 16),
              const ExpressiveLoadingIndicator(
                style: ExpressiveLoadingIndicatorStyle.outlined,
                color: Colors.teal,
                strokeWidth: 4,
              ),

              const SizedBox(height: 32),
              const Text(
                'Custom color (filled):',
                style: TextStyle(fontSize: 16),
              ),
              const SizedBox(height: 16),
              const ExpressiveLoadingIndicator(color: Colors.orange),

              const SizedBox(height: 32),
              const Text(
                'Custom size (filled):',
                style: TextStyle(fontSize: 16),
              ),
              const SizedBox(height: 16),
              const ExpressiveLoadingIndicator(
                constraints: BoxConstraints(
                  minWidth: 72.0,
                  minHeight: 72.0,
                  maxWidth: 72.0,
                  maxHeight: 72.0,
                ),
              ),

              const SizedBox(height: 32),
              const Text(
                'Custom shapes (filled):',
                style: TextStyle(fontSize: 16),
              ),
              const SizedBox(height: 16),
              ExpressiveLoadingIndicator(
                polygons: [
                  MaterialShapes.softBurst,
                  MaterialShapes.pill,
                  MaterialShapes.pentagon,
                ],
              ),

              const SizedBox(height: 32),
              const Text(
                'Same shapes (outlined):',
                style: TextStyle(fontSize: 16),
              ),
              const SizedBox(height: 16),
              ExpressiveLoadingIndicator(
                style: ExpressiveLoadingIndicatorStyle.outlined,
                polygons: [
                  MaterialShapes.softBurst,
                  MaterialShapes.pill,
                  MaterialShapes.pentagon,
                ],
              ),

              const SizedBox(height: 40),
              const Divider(),
              const SizedBox(height: 24),
              const Text(
                'Expressive linear (M3 wavy)',
                style: TextStyle(fontSize: 18, fontWeight: FontWeight.w600),
              ),
              const SizedBox(height: 8),
              const Text('Indeterminate', style: TextStyle(fontSize: 14)),
              const SizedBox(height: 8),
              Padding(
                padding: const EdgeInsets.symmetric(horizontal: 24.0),
                child: ExpressiveLinearProgressIndicator(),
              ),
              const SizedBox(height: 24),
              Text(
                'Determinate (${(linearProgress * 100).round()}%) — drag slider',
                textAlign: TextAlign.center,
                style: const TextStyle(fontSize: 14),
              ),
              const SizedBox(height: 8),
              Padding(
                padding: const EdgeInsets.symmetric(horizontal: 24.0),
                child: ExpressiveLinearProgressIndicator(value: linearProgress),
              ),
              Padding(
                padding: const EdgeInsets.symmetric(horizontal: 16.0),
                child: Slider(
                  value: linearProgress,
                  onChanged: (v) {
                    _hapticIfSliderBucketChanged(
                      previous: linearProgress,
                      next: v,
                      buckets: 20,
                    );
                    ref.read(linearDemoProgressProvider.notifier).state = v;
                  },
                ),
              ),
              const SizedBox(height: 24),
              Text(
                'Taller bar, amplitude ${linearAmplitude.toStringAsFixed(2)}',
                textAlign: TextAlign.center,
                style: const TextStyle(fontSize: 14),
              ),
              const SizedBox(height: 8),
              Padding(
                padding: const EdgeInsets.symmetric(horizontal: 24.0),
                child: ExpressiveLinearProgressIndicator(
                  minHeight: 14,
                  amplitude: linearAmplitude,
                  value: 0.72,
                ),
              ),
              Padding(
                padding: const EdgeInsets.symmetric(horizontal: 16.0),
                child: Slider(
                  value: linearAmplitude,
                  max: 1,
                  divisions: 20,
                  label: linearAmplitude.toStringAsFixed(2),
                  onChanged: (v) {
                    _hapticIfSliderBucketChanged(
                      previous: linearAmplitude,
                      next: v,
                      buckets: 20,
                    );
                    ref.read(linearDemoAmplitudeProvider.notifier).state = v;
                  },
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

/// Fires a light selection haptic when [next] crosses into another bucket vs
/// [previous]. Matches discrete [Slider] steps (e.g. [divisions]: 20 → 20 buckets).
///
/// Uses [HapticFeedback.selectionClick], which is implemented for iOS and
/// Android in Flutter; desktop/web typically no-op.
void _hapticIfSliderBucketChanged({
  required double previous,
  required double next,
  required int buckets,
}) {
  final prev = (previous * buckets).round();
  final nxt = (next * buckets).round();
  if (prev != nxt) {
    HapticFeedback.selectionClick();
  }
}
2
likes
0
points
162
downloads

Publisher

unverified uploader

Weekly Downloads

Material Design 3 expressive loading and progress indicators for Flutter: morphing circular indicator, outlined variant, and wavy linear progress.

Repository (GitHub)
View/report issues

Topics

#ui #material-design #loading-indicator #progress-indicator #animation

License

unknown (license)

Dependencies

flutter, material_new_shapes

More

Packages that depend on material3_expressive_loading_indicator