Skip to content

Instantly share code, notes, and snippets.

@callmephil
Created October 31, 2025 19:48
Show Gist options
  • Select an option

  • Save callmephil/f219dc01768ae320744938682f22f24f to your computer and use it in GitHub Desktop.

Select an option

Save callmephil/f219dc01768ae320744938682f22f24f to your computer and use it in GitHub Desktop.
deintegrate effect.
// ignore_for_file: prefer-single-widget-per-file
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter_animate/flutter_animate.dart';
void main() => runApp(const MaterialApp(home: ThanosSnapDemo()));
class ThanosSnapDemo extends StatefulWidget {
const ThanosSnapDemo({super.key});
@override
State<ThanosSnapDemo> createState() => _ThanosSnapDemoState();
}
class _ThanosSnapDemoState extends State<ThanosSnapDemo> {
bool snapped = false;
@override
Widget build(BuildContext context) {
const text =
'Hmm. We’re having trouble finding that site.'
'Please check the URL or try again later.'
'If the problem persists, contact support.';
final chars = text.split('');
return Scaffold(
backgroundColor: Colors.black,
body: Center(
child: Padding(
padding: const EdgeInsets.all(8),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () => setState(() => snapped = !snapped),
child: const Text('Snap'),
),
Wrap(
key: ValueKey(snapped),
alignment: WrapAlignment.center,
children: [
for (int i = 0; i < chars.length; i++)
_AnimatedChar(
char: chars[i],
index: i,
snapped: snapped,
),
],
),
],
),
),
),
);
}
}
class _AnimatedChar extends StatelessWidget {
const _AnimatedChar({
required this.char,
required this.index,
required this.snapped,
});
final String char;
final int index;
final bool snapped;
// Static random group assignment for all chars
static final List<int> _groups = [];
static int _getGroup(int index, int totalChars, int groupCount) {
// Fill _groups only once
if (_groups.length != totalChars) {
_groups.clear();
final rand = Random(42); // Seed for repeatability
for (var i = 0; i < totalChars; i++) {
_groups.add(rand.nextInt(groupCount));
}
}
return _groups[index];
}
@override
Widget build(BuildContext context) {
final random = Random(index);
final dx = (random.nextDouble() - 0.5) * 200;
final dyDirection = random.nextBool() ? 1 : -1;
final dy = random.nextDouble() * 300 * dyDirection;
final blurRadius =
random.nextDouble() * 4 + 2; // Lower blur for performance
// Get total chars and group count from context
final totalChars =
context.findAncestorWidgetOfExactType<Wrap>()?.children.length ?? 100;
const groupCount = 6; // e.g. 6 random groups
final group = _getGroup(index, totalChars, groupCount);
final delay = Duration(milliseconds: group * 120);
return RepaintBoundary(
child:
Text(
char,
style: const TextStyle(
fontSize: 36,
color: Colors.white,
fontWeight: FontWeight.bold,
),
)
.animate()
.move(
begin: snapped ? Offset.zero : Offset(dx, dy),
end: snapped ? Offset(dx, dy) : Offset.zero,
curve: Curves.easeInOutCubic,
duration: 800.ms,
delay: delay,
)
.fade(
begin: snapped ? 1 : 0,
end: snapped ? 0 : 1,
duration: 1200.ms,
delay: delay,
)
.blur(
begin: snapped ? Offset.zero : Offset(blurRadius, blurRadius),
end: snapped ? Offset(blurRadius, blurRadius) : Offset.zero,
duration: 400.ms,
delay: delay,
)
.scale(
begin: snapped ? const Offset(1, 1) : const Offset(0.8, 0.8),
end: const Offset(0.8, 0.8),
duration: 800.ms,
delay: delay,
),
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment