particle_text 0.0.1 copy "particle_text: ^0.0.1" to clipboard
particle_text: ^0.0.1 copied to clipboard

Interactive particle text effect for Flutter. Particles form text shapes and scatter on touch/hover, with spring-based physics and full customization.

example/lib/main.dart

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

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

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

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

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

  @override
  State<DemoScreen> createState() => _DemoScreenState();
}

class _DemoScreenState extends State<DemoScreen> {
  String _text = 'Flutter';
  bool _isEditing = false;
  late TextEditingController _controller;
  int _presetIndex = 0;

  final List<_Preset> _presets = [
    _Preset('Default', const ParticleConfig()),
    _Preset('Cosmic', ParticleConfig.cosmic()),
    _Preset('Fire', ParticleConfig.fire()),
    _Preset('Matrix', ParticleConfig.matrix()),
    _Preset('Pastel', ParticleConfig.pastel()),
    _Preset('Minimal', ParticleConfig.minimal()),
  ];

  @override
  void initState() {
    super.initState();
    _controller = TextEditingController(text: _text);
  }

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

  @override
  Widget build(BuildContext context) {
    final preset = _presets[_presetIndex];

    return Scaffold(
      backgroundColor: preset.config.backgroundColor,
      body: Stack(
        children: [
          // Particle effect (full screen)
          ParticleText(
            text: _text,
            config: preset.config,
          ),

          // Top: hint text
          Positioned(
            top: MediaQuery.of(context).padding.top + 12,
            left: 0,
            right: 0,
            child: Center(
              child: Text(
                'TOUCH & DRAG TO INTERACT',
                style: TextStyle(
                  color: Colors.white.withValues(alpha: 0.15),
                  fontSize: 11,
                  letterSpacing: 3,
                ),
              ),
            ),
          ),

          // Bottom: controls
          Positioned(
            bottom: MediaQuery.of(context).padding.bottom + 16,
            left: 16,
            right: 16,
            child: Column(
              mainAxisSize: MainAxisSize.min,
              children: [
                // Preset selector
                SizedBox(
                  height: 36,
                  child: ListView.separated(
                    scrollDirection: Axis.horizontal,
                    shrinkWrap: true,
                    itemCount: _presets.length,
                    separatorBuilder: (_, __) => const SizedBox(width: 8),
                    itemBuilder: (context, index) {
                      final isSelected = index == _presetIndex;
                      return GestureDetector(
                        onTap: () => setState(() => _presetIndex = index),
                        child: Container(
                          padding: const EdgeInsets.symmetric(
                            horizontal: 14,
                            vertical: 8,
                          ),
                          decoration: BoxDecoration(
                            color: isSelected
                                ? Colors.white.withValues(alpha: 0.12)
                                : Colors.white.withValues(alpha: 0.04),
                            borderRadius: BorderRadius.circular(8),
                            border: Border.all(
                              color: isSelected
                                  ? Colors.white.withValues(alpha: 0.25)
                                  : Colors.white.withValues(alpha: 0.08),
                            ),
                          ),
                          child: Text(
                            _presets[index].name,
                            style: TextStyle(
                              color: isSelected
                                  ? Colors.white.withValues(alpha: 0.8)
                                  : Colors.white.withValues(alpha: 0.35),
                              fontSize: 12,
                              letterSpacing: 0.5,
                            ),
                          ),
                        ),
                      );
                    },
                  ),
                ),

                const SizedBox(height: 12),

                // Text input
                _isEditing
                    ? Container(
                        width: 220,
                        decoration: BoxDecoration(
                          color: Colors.white.withValues(alpha: 0.06),
                          borderRadius: BorderRadius.circular(8),
                          border: Border.all(
                            color: Colors.white.withValues(alpha: 0.15),
                          ),
                        ),
                        child: TextField(
                          controller: _controller,
                          autofocus: true,
                          textAlign: TextAlign.center,
                          style: TextStyle(
                            color: Colors.white.withValues(alpha: 0.8),
                            fontSize: 14,
                          ),
                          decoration: const InputDecoration(
                            border: InputBorder.none,
                            contentPadding: EdgeInsets.symmetric(
                              horizontal: 16,
                              vertical: 10,
                            ),
                          ),
                          onSubmitted: (val) {
                            if (val.trim().isNotEmpty) {
                              setState(() {
                                _text = val.trim();
                                _isEditing = false;
                              });
                            }
                          },
                        ),
                      )
                    : GestureDetector(
                        onTap: () => setState(() {
                          _isEditing = true;
                          _controller.text = _text;
                        }),
                        child: Container(
                          padding: const EdgeInsets.symmetric(
                            horizontal: 24,
                            vertical: 10,
                          ),
                          decoration: BoxDecoration(
                            color: Colors.white.withValues(alpha: 0.04),
                            borderRadius: BorderRadius.circular(8),
                            border: Border.all(
                              color: Colors.white.withValues(alpha: 0.08),
                            ),
                          ),
                          child: Text(
                            'change text',
                            style: TextStyle(
                              color: Colors.white.withValues(alpha: 0.35),
                              fontSize: 13,
                              letterSpacing: 1,
                            ),
                          ),
                        ),
                      ),
              ],
            ),
          ),
        ],
      ),
    );
  }
}

class _Preset {
  final String name;
  final ParticleConfig config;

  const _Preset(this.name, this.config);
}
6
likes
0
points
166
downloads

Publisher

verified publishermindwaveinfoway.com

Weekly Downloads

Interactive particle text effect for Flutter. Particles form text shapes and scatter on touch/hover, with spring-based physics and full customization.

Repository (GitHub)
View/report issues

License

unknown (license)

Dependencies

flutter

More

Packages that depend on particle_text