You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
samples/next_gen_ui_demo/lib/orb_shader/orb_shader_widget.dart

124 lines
4.0 KiB

// Copyright 2023 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:async';
import 'dart:math' as math;
import 'dart:ui' as ui;
import 'package:flutter/material.dart';
import 'package:flutter_animate/flutter_animate.dart';
import 'package:provider/provider.dart';
import '../assets.dart';
import '../common/reactive_widget.dart';
import 'orb_shader_config.dart';
import 'orb_shader_painter.dart';
class OrbShaderWidget extends StatefulWidget {
const OrbShaderWidget({
super.key,
required this.config,
this.onUpdate,
required this.mousePos,
required this.minEnergy,
});
final double minEnergy;
final OrbShaderConfig config;
final Offset mousePos;
final void Function(double energy)? onUpdate;
@override
State<OrbShaderWidget> createState() => OrbShaderWidgetState();
}
class OrbShaderWidgetState extends State<OrbShaderWidget>
with SingleTickerProviderStateMixin {
final _heartbeatSequence = TweenSequence(
[
TweenSequenceItem(tween: ConstantTween(0), weight: 40),
TweenSequenceItem(
tween: Tween(begin: 0.0, end: 1.0)
.chain(CurveTween(curve: Curves.easeInOutCubic)),
weight: 8),
TweenSequenceItem(
tween: Tween(begin: 1.0, end: 0.2)
.chain(CurveTween(curve: Curves.easeInOutCubic)),
weight: 12),
TweenSequenceItem(
tween: Tween(begin: 0.2, end: 0.8)
.chain(CurveTween(curve: Curves.easeInOutCubic)),
weight: 6),
TweenSequenceItem(
tween: Tween(begin: 0.8, end: 0.0)
.chain(CurveTween(curve: Curves.easeInOutCubic)),
weight: 10),
],
);
late final AnimationController _heartbeatAnim;
@override
void initState() {
super.initState();
_heartbeatAnim = AnimationController(vsync: this, duration: 3000.ms)
..repeat();
}
@override
void dispose() {
_heartbeatAnim.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) => Consumer<Shaders?>(
builder: (context, shaders, _) {
if (shaders == null) return const SizedBox.expand();
return ListenableBuilder(
listenable: _heartbeatAnim,
builder: (_, __) {
final heartbeatEnergy =
_heartbeatAnim.drive(_heartbeatSequence).value;
return TweenAnimationBuilder(
tween: Tween<double>(
begin: widget.minEnergy, end: widget.minEnergy),
duration: 300.ms,
curve: Curves.easeOutCubic,
builder: (context, minEnergy, child) {
return ReactiveWidget(
builder: (context, time, size) {
double energyLevel = 0;
if (size.shortestSide != 0) {
final d = (Offset(size.width, size.height) / 2 -
widget.mousePos)
.distance;
final hitSize = size.shortestSide * .5;
energyLevel = 1 - math.min(1, (d / hitSize));
scheduleMicrotask(
() => widget.onUpdate?.call(energyLevel));
}
energyLevel +=
(1.3 - energyLevel) * heartbeatEnergy * 0.1;
energyLevel = ui.lerpDouble(minEnergy, 1, energyLevel)!;
return CustomPaint(
size: size,
painter: OrbShaderPainter(
shaders.orb,
config: widget.config,
time: time,
mousePos: widget.mousePos,
energy: energyLevel,
),
);
},
);
},
);
},
);
},
);
}