flutter_animate_plus 5.1.0
flutter_animate_plus: ^5.1.0 copied to clipboard
A maintained fork of flutter_animate with beautiful animated effects and builders for Flutter.
import 'dart:ui';
import 'package:flutter/material.dart';
import 'about_page.dart';
import 'demo_playback.dart';
import 'examples/adapter_view.dart';
import 'examples/everything_view.dart';
import 'examples/info_view.dart';
import 'examples/playground_view.dart';
import 'examples/visual_view.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
try {
final program = await FragmentProgram.fromAsset(
'assets/shaders/shader.frag',
);
EverythingView.shader = program.fragmentShader();
} catch (error, stack) {
FlutterError.reportError(
FlutterErrorDetails(exception: error, stack: stack),
);
}
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key, this.initialTab = 0});
final int initialTab;
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Animate Plus',
debugShowCheckedModeBanner: false,
theme: ThemeData(
useMaterial3: true,
brightness: Brightness.dark,
colorScheme: ColorScheme.fromSeed(
seedColor: const Color(0xFF80DDFF),
brightness: Brightness.dark,
surface: const Color(0xFF141A22),
),
scaffoldBackgroundColor: const Color(0xFF10151C),
),
home: FlutterAnimateExample(initialTab: initialTab),
);
}
}
class FlutterAnimateExample extends StatefulWidget {
const FlutterAnimateExample({super.key, this.initialTab = 0})
: assert(initialTab >= 0 && initialTab < 5);
final int initialTab;
static final List<TabInfo> tabs = [
TabInfo(
Icons.auto_awesome_outlined,
(_) => const InfoView(),
'Overview',
'Compose expressive motion with a few lines of Dart.',
),
TabInfo(
Icons.palette_outlined,
(_) => const VisualView(),
'Visual',
'Watch saturation, tint and blur work together.',
),
TabInfo(
Icons.tune,
(_) => const AdapterView(),
'Adapters',
'Drive animation with a slider or scroll position.',
),
TabInfo(
Icons.grid_view_outlined,
(_) => const EverythingView(),
'Effects',
'Explore individual effects and layered combinations.',
),
TabInfo(
Icons.science_outlined,
(_) => const PlaygroundView(),
'Playground',
'A small starting point for your next animation.',
),
];
@override
State<FlutterAnimateExample> createState() => _FlutterAnimateExampleState();
}
class _FlutterAnimateExampleState extends State<FlutterAnimateExample> {
late int _viewIndex = widget.initialTab;
int _replay = 0;
bool _playing = true;
bool _allowMotion = false;
void _select(int index) => setState(() {
_viewIndex = index;
_replay++;
});
@override
Widget build(BuildContext context) {
final tab = FlutterAnimateExample.tabs[_viewIndex];
final reducedMotion = MediaQuery.disableAnimationsOf(context);
final staticMode = reducedMotion && !_allowMotion;
return LayoutBuilder(
builder: (context, constraints) {
final useRail =
constraints.maxWidth >= 720 && constraints.maxHeight >= 500;
final content = Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Padding(
padding: const EdgeInsets.fromLTRB(24, 20, 12, 16),
child: Row(
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
tab.label,
style: Theme.of(context).textTheme.titleLarge,
),
if (constraints.maxHeight >= 500 &&
MediaQuery.textScalerOf(context).scale(14) <
20) ...[
const SizedBox(height: 4),
Text(
tab.description,
style: const TextStyle(color: Color(0xFFAEBBCB)),
),
],
],
),
),
if (!staticMode)
IconButton(
tooltip: _playing
? 'Pause animation'
: 'Resume animation',
onPressed: () => setState(() => _playing = !_playing),
icon: Icon(_playing ? Icons.pause : Icons.play_arrow),
),
IconButton(
tooltip: 'Replay animation',
onPressed: () {
if (staticMode) {
setState(() {
_allowMotion = true;
_playing = true;
_replay++;
});
} else {
setState(() {
_playing = true;
_replay++;
});
}
},
icon: const Icon(Icons.replay),
),
IconButton(
tooltip: 'About Flutter Animate Plus',
onPressed: () => Navigator.of(context).push(
MaterialPageRoute<void>(
builder: (_) => const AboutPage(),
),
),
icon: const Icon(Icons.info_outline),
),
],
),
),
if (reducedMotion)
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Wrap(
crossAxisAlignment: WrapCrossAlignment.center,
spacing: 8,
children: [
Text(
staticMode
? 'Reduced motion · static previews'
: 'Motion enabled for this session',
),
TextButton(
onPressed: () => setState(() {
_allowMotion = !_allowMotion;
_playing = _allowMotion;
}),
child: Text(
staticMode ? 'Enable demos' : 'Use static previews',
),
),
],
),
),
const Divider(height: 1),
Expanded(
child: KeyedSubtree(
key: ValueKey('$_viewIndex:$_replay'),
child: DemoPlayback(
playing: _playing,
staticMode: staticMode,
child: _viewIndex == 0
? InfoView(onNavigate: _select)
: tab.builder(context),
),
),
),
],
);
return Scaffold(
bottomNavigationBar: useRail
? null
: NavigationBar(
selectedIndex: _viewIndex,
onDestinationSelected: _select,
labelBehavior: NavigationDestinationLabelBehavior.alwaysShow,
destinations: [
for (final item in FlutterAnimateExample.tabs)
NavigationDestination(
icon: Icon(item.icon),
label: item.label,
),
],
),
body: SafeArea(
bottom: useRail,
child: DefaultTextStyle(
style: const TextStyle(
fontFamily: 'Roboto',
color: Color(0xFFE6EDF5),
fontSize: 14,
height: 1.5,
),
child: Row(
children: [
if (useRail) ...[
NavigationRail(
selectedIndex: _viewIndex,
onDestinationSelected: _select,
labelType: NavigationRailLabelType.all,
leading: const Padding(
padding: EdgeInsets.symmetric(vertical: 20),
child: Icon(
Icons.animation,
color: Color(0xFF80DDFF),
size: 32,
),
),
destinations: [
for (final item in FlutterAnimateExample.tabs)
NavigationRailDestination(
icon: Icon(item.icon),
label: Text(item.label),
),
],
),
const VerticalDivider(width: 1),
],
Expanded(child: content),
],
),
),
),
);
},
);
}
}
class TabInfo {
const TabInfo(this.icon, this.builder, this.label, this.description);
final IconData icon;
final WidgetBuilder builder;
final String label;
final String description;
}