constellation_particles 1.1.0 copy "constellation_particles: ^1.1.0" to clipboard
constellation_particles: ^1.1.0 copied to clipboard

Pointer-reactive constellation particle background for Flutter. Honours reduced motion and high contrast, pauses when backgrounded, zero runtime deps.

example/lib/main.dart

import 'package:constellation_particles/constellation_particles.dart';
import 'package:flutter/material.dart';

void main() => runApp(const ExampleApp());

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      theme: ThemeData.dark(useMaterial3: true),
      home: const FieldPage(),
    );
  }
}

/// A 140-particle field, with a switch that asks it to hold still.
///
/// Reduced motion is the behaviour worth seeing before you adopt this widget,
/// and reaching it through the OS accessibility panel takes you out of the
/// app. The switch puts the same request in front of the field instead. Flip
/// it and watch what the field does with it: the points stop drifting and stay
/// where they are. The background does not disappear, which is what makes this
/// different from hiding the widget when motion is unwelcome.
class FieldPage extends StatefulWidget {
  const FieldPage({super.key});

  @override
  State<FieldPage> createState() => _FieldPageState();
}

class _FieldPageState extends State<FieldPage> {
  bool _askForStillness = false;

  @override
  Widget build(BuildContext context) {
    final platformAsked = MediaQuery.disableAnimationsOf(context);

    // Or-ed with the platform rather than replacing it. The switch can only
    // add the request: if you already have reduce-motion on in the OS, turning
    // this one off leaves the field held, because an app has no business
    // overriding that setting downwards.
    final holdStill = platformAsked || _askForStillness;

    return Scaffold(
      backgroundColor: const Color(0xFF0A0E14),
      body: Stack(
        children: [
          Positioned.fill(
            // The override wraps the field alone. Applied to the whole page it
            // would hold the switch's own thumb still as well, and the control
            // you are experimenting with would stop responding to you.
            child: MediaQuery(
              data:
                  MediaQuery.of(context).copyWith(disableAnimations: holdStill),
              child: const ConstellationParticles(
                particleCount: 140,
                color: Color(0xFF64FFDA),
                speed: 1.1,
              ),
            ),
          ),
          Center(
            child: Text(
              holdStill
                  ? 'the drift stopped, the field stayed'
                  : 'move your cursor',
              textAlign: TextAlign.center,
              style: Theme.of(context)
                  .textTheme
                  .titleMedium
                  ?.copyWith(color: Colors.white24, letterSpacing: 2),
            ),
          ),
          Align(
            alignment: Alignment.bottomCenter,
            child: SafeArea(
              child: Padding(
                padding: const EdgeInsets.only(bottom: 24),
                child: _StillnessControl(
                  value: _askForStillness,
                  platformAsked: platformAsked,
                  onChanged: (v) => setState(() => _askForStillness = v),
                ),
              ),
            ),
          ),
        ],
      ),
    );
  }
}

class _StillnessControl extends StatelessWidget {
  const _StillnessControl({
    required this.value,
    required this.platformAsked,
    required this.onChanged,
  });

  final bool value;
  final bool platformAsked;
  final ValueChanged<bool> onChanged;

  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context);
    return Column(
      mainAxisSize: MainAxisSize.min,
      children: [
        Row(
          mainAxisSize: MainAxisSize.min,
          children: [
            Switch(value: value, onChanged: onChanged),
            const SizedBox(width: 12),
            Text(
              'ask for reduced motion',
              style: theme.textTheme.bodyLarge?.copyWith(color: Colors.white54),
            ),
          ],
        ),
        if (platformAsked)
          Padding(
            padding: const EdgeInsets.only(top: 6),
            child: Text(
              'your system already asks for it; the field is held either way',
              style: theme.textTheme.bodySmall?.copyWith(color: Colors.white24),
            ),
          ),
      ],
    );
  }
}
1
likes
160
points
749
downloads
screenshot

Documentation

API reference

Publisher

verified publisherdeveloperyusuf.com

Weekly Downloads

Pointer-reactive constellation particle background for Flutter. Honours reduced motion and high contrast, pauses when backgrounded, zero runtime deps.

Repository (GitHub)
View/report issues

Topics

#animation #particles #canvas #background #custompainter

License

MIT (license)

Dependencies

flutter

More

Packages that depend on constellation_particles