State machines og inputstyret bevægelse
Forbind input fra Rive-state machines med appdata for reaktive animationer med forgreninger.
State machines og inputstyret bevægelse er en gratis Mobiludvikling med Flutter-lektion på CoddyKit. Dette er lektion 2 af 4. Du kan læse alle 3 lektioner i dette læringsspor gratis i deres fulde længde — derefter låser CoddyKit PRO alle lektioner op samt praktiske øvelser med en indbygget kodeeditor og en AI-underviser døgnet rundt. Den er en del af læringsforløbet i Mobiludvikling med Flutter, og dine fremskridt synkroniseres på tværs af nettet og CoddyKit-appen. Mobiludvikling med Flutter-kurset indeholder 4 lektioner i alt.
Hvorfor tilstandsmaskiner er bedre end lineære animationer
Traditionelle Rive-animationer afspiller én tidslinje fra start til slut. En tilstandsmaskine er en graf af animationstilstande, der er forbundet med overgange, og disse overgange udløses af inputværdier, som du styrer fra Dart.
- Reaktiv: Animationen afspejler aktuelle data fra appen i stedet for en fast sekvens.
- Forgrening: Den samme tegneflade kan være inaktiv, reagere på markøren, vise succes eller vise fejl afhængigt af inputværdierne.
- Blød overgang: Rive interpolerer mellem tilstande, så du får jævn bevægelse uden ekstra arbejde.
I denne lektion kobler du inputværdierne i en Rive-tilstandsmaskine til dit widgettræ, så bevægelserne følger brugerhandlinger og modellens tilstand.
De tre inputtyper
En Rive-tilstandsmaskine stiller præcis tre slags inputværdier til rådighed, og valget af den rigtige type er den centrale designbeslutning.
- Boolesk værdi (
SMIBool): En fastholdt til/fra-værdi, f.eks.isOpenellerisLoading. - Tal (
SMINumber): En kontinuerlig værdi, der kan styre blandingstilstande, f.eks. en fremdrift fra 0 til 100 eller en rulleforskydning. - Udløser (
SMITrigger): En enkeltstående impuls, der udløser en overgang én gang, f.eks.tapSuccess. Den har ingen vedvarende værdi.
Tommelfingerregel: Brug en udløser til øjeblikkelige hændelser, en boolesk værdi til en tilstand, du fastholder, og et tal til alt kontinuerligt, som du vil blande.
Indlæsning af tegnefladen og tilstandsmaskinen
For at styre inputværdier skal du bruge en StateMachineController. Callbacken onInit fra RiveAnimation.asset giver dig den indlæste Artboard. Du opretter en styreenhed ud fra en navngiven tilstandsmaskine og tilknytter den.
Hent altid referencer til de inputværdier, du har brug for, netop her, mens du stadig har tegnefladen, og gem dem i din widgets tilstand.
import 'package:flutter/material.dart';
import 'package:rive/rive.dart';
class LikeButton extends StatefulWidget {
const LikeButton({super.key});
@override
State<LikeButton> createState() => _LikeButtonState();
}
class _LikeButtonState extends State<LikeButton> {
StateMachineController? _controller;
SMIBool? _isLiked;
void _onRiveInit(Artboard artboard) {
final controller = StateMachineController.fromArtboard(
artboard,
'LikeMachine',
);
if (controller != null) {
artboard.addController(controller);
_controller = controller;
_isLiked = controller.findInput<bool>('isLiked') as SMIBool?;
}
}
@override
Widget build(BuildContext context) {
return RiveAnimation.asset(
'assets/like.riv',
stateMachines: const ['LikeMachine'],
onInit: _onRiveInit,
);
}
}Indstilling af en boolesk inputværdi ved tryk
Når du har en reference til en SMIBool, styrer du den blot ved at tildele dens value. Rive evaluerer straks de overgange, hvis betingelse afhænger af denne inputværdi.
Pak Rive-widgetten ind i en GestureDetector, og skift den booleske værdi. Du behøver ikke kalde setState for selve animationen — Rive tegner internt igen — men du kan få brug for det for at holde din egen model synkroniseret.
GestureDetector(
onTap: () {
final input = _isLiked;
if (input != null) {
input.value = !input.value; // flips the held state
}
},
child: RiveAnimation.asset(
'assets/like.riv',
stateMachines: const ['LikeMachine'],
onInit: _onRiveInit,
),
)Udløsning af en udløser til en enkeltstående bevægelse
En SMITrigger har ingen value. Du kalder fire() for at sende en impuls præcis én gang, hvilket er perfekt til konfettieksplosioner, succesmarkeringer eller rystelser ved fejl.
Da udløsere nulstilles automatisk, behøver du aldrig rydde dem. Hvis du udløser igen, afspilles overgangen på ny.
class _SubmitButtonState extends State<SubmitButton> {
SMITrigger? _success;
SMITrigger? _error;
void _onInit(Artboard artboard) {
final c = StateMachineController.fromArtboard(artboard, 'SubmitMachine');
if (c != null) {
artboard.addController(c);
_success = c.findSMI('success') as SMITrigger?;
_error = c.findSMI('error') as SMITrigger?;
}
}
Future<void> _onSubmit() async {
final ok = await _saveForm();
if (ok) {
_success?.fire();
} else {
_error?.fire();
}
}
}Styring af en talinputværdi til blandede tilstande
Talinput er ideelle, når bevægelsen skal følge en kontinuerlig værdi. En blend-tilstand i Rive kan ændre et artboard gradvist over området 0–100, så live-data giver flydende, datadrevet bevægelse.
Almindelige kilder er downloadfremdrift, en rulleposition, en skyder eller en sensormåling. Begræns værdien, så den aldrig overskrider det område, kunstneren har designet.
class _ProgressGaugeState extends State<ProgressGauge> {
SMINumber? _progress;
void _onInit(Artboard artboard) {
final c = StateMachineController.fromArtboard(artboard, 'GaugeMachine');
if (c != null) {
artboard.addController(c);
_progress = c.findSMI('progress') as SMINumber?;
}
}
void updateProgress(double fraction) {
// fraction is 0.0..1.0 from a download stream
_progress?.value = (fraction * 100).clamp(0, 100);
}
}Forbindelse af input til en strøm af appdata
Inputstyret bevægelse bliver virkelig effektiv, når en datastrøm leverer data til den. Abonner i initState, send hver hændelse ind i det tilsvarende input, og annullér abonnementet i dispose for at undgå lækager.
Bemærk, at du beskytter dig mod et null-input: Datastrømmen kan udsende data, før onInit er kørt, så et null-bevidst kald holder dig på den sikre side.
late final StreamSubscription<double> _sub;
@override
void initState() {
super.initState();
_sub = downloadProgress$.listen((fraction) {
_progress?.value = (fraction * 100).clamp(0, 100);
});
}
@override
void dispose() {
_sub.cancel();
_controller?.dispose();
super.dispose();
}Modellering af forgreningslogik i ren Dart
Før du tager fat på Rive, er det nyttigt at modellere den beslutning, der vælger en gren, som almindelig Dart. Her er en selvstændig mapper, der omdanner et uploadresultat til den type inputhændelse, du ville udløse. Når du tester denne logik isoleret, kan din widget holdes enkel.
Dette kodestykke har ingen Flutter- eller Rive-afhængighed, så det kan køre overalt.
enum MotionEvent { idle, loading, success, error }
MotionEvent selectMotion({
required bool inFlight,
required bool? succeeded,
}) {
if (inFlight) return MotionEvent.loading;
if (succeeded == null) return MotionEvent.idle;
return succeeded ? MotionEvent.success : MotionEvent.error;
}
void main() {
print(selectMotion(inFlight: true, succeeded: null)); // loading
print(selectMotion(inFlight: false, succeeded: true)); // success
print(selectMotion(inFlight: false, succeeded: false)); // error
print(selectMotion(inFlight: false, succeeded: null)); // idle
}Anvendelse af en grenbeslutning på input
Knyt nu den rene beslutning til tilstandsmaskinen. En enkelt metode oversætter en MotionEvent til den korrekte kombination af booleske tilstande og triggerimpulser.
- Booleske værdier fastholder en tilstand, så indstil både den nye værdi og ryd den modsatte værdi, hvor det er nødvendigt.
- Triggere udløses én gang for midlertidig feedback.
Ved at samle dette i én metode bliver din inputforbindelse ensartet og nem at gennemgå.
void applyMotion(MotionEvent event) {
switch (event) {
case MotionEvent.loading:
_isLoading?.value = true;
break;
case MotionEvent.idle:
_isLoading?.value = false;
break;
case MotionEvent.success:
_isLoading?.value = false;
_success?.fire();
break;
case MotionEvent.error:
_isLoading?.value = false;
_error?.fire();
break;
}
}Læs inputtilstanden tilbage via lyttere
Nogle gange styres bevægelsen af Rive selv, for eksempel en trækbar knap, som brugeren flytter på artboardet, og så har du brug for at få den aktuelle værdi tilbage i Dart. Input viser deres værdi, og du kan enten hente den løbende eller reagere på den i tilstandsmaskinens ændringscallback.
StateMachineController accepterer også en onStateChange-callback, så du kan køre Dart, hver gang den aktive tilstand skifter. Det er nyttigt til analyse eller kædning af sideeffekter.
void _onInit(Artboard artboard) {
final c = StateMachineController.fromArtboard(
artboard,
'KnobMachine',
onStateChange: (machineName, stateName) {
debugPrint('Entered $stateName in $machineName');
if (stateName == 'Snapped') {
HapticFeedback.lightImpact();
}
},
);
if (c != null) {
artboard.addController(c);
_angle = c.findSMI('angle') as SMINumber?;
}
}Livscyklus, frigivelse og almindelige faldgruber
Tilstandsmaskiner er ressourcer med en tilstand. Behandl dem med samme disciplin som controllere og abonnementer.
- Frigiv altid
StateMachineControlleridispose()for at frigive bindingen til artboardet. - Kontrollér input for null:
findSMI/findInputreturnerer null, hvis navnet er stavet forkert, eller artboardet ikke har det pågældende input — ellers sker der ingenting uden en fejl. - Match navne nøjagtigt: Navne på input og tilstandsmaskiner skelner mellem store og små bogstaver og skal stemme overens med
.riv-filen. - Undgå for mange kald til setState: Rive tegner sig selv igen; det spilder frames at genopbygge hele undertræet ved hvert tick fra datastrømmen.
Når du gør dette korrekt, forbliver din inputstyrede bevægelse jævn og fri for lækager.
Hurtigt tjek: Vælg det rigtige input
Du forbinder en Rive-tilstandsmaskine med en knap til afsendelse af en formular. Når brugeren trykker på knappen, skal der afspilles en succesanimation med et flueben én gang, hvorefter den vender tilbage til inaktiv tilstand. Hvilken inputtype og hvilket kald er korrekt?
Opsummering: Reaktiv, forgrenet bevægelse
Du ved nu, hvordan du får Rive-animationer til at følge din app i stedet for en fast tidslinje.
- Tre inputtyper: Boolean til en fastholdt tilstand, Number til kontinuerlige eller blendede værdier og Trigger til engangshændelser.
- Forbindelse: Opret en
StateMachineControllerionInit, tilknyt den til artboardet, og gem referencer til input. - Styring: Tildel til
valuefor booleske værdier og tal, kaldfire()for triggere, og send kontinuerlige data fra datastrømme med begrænsning. - Arkitektur: Modellér grenbeslutningen i ren, testbar Dart, og oversæt den derefter til input i én central metode.
- Vedligeholdelse: Frigiv controlleren, annullér abonnementer, kontrollér input for null, og match navne nøjagtigt.
Med disse mønstre bliver dine animationer en levende afspejling af brugerinput og modeltilstand.
Lær Dart med en AI-underviser — gratis
Skriv og kør rigtig kode i din browser, få øjeblikkelig hjælp fra en AI-underviser døgnet rundt, og fortsæt, hvor du slap, på web eller i appen.
- Kurser
- 22
- Lektioner
- 88
Ofte stillede spørgsmål
Er lektionen “State machines og inputstyret bevægelse” gratis?
Ja — alle 3 lektioner i læringssporet Mobiludvikling med Flutter, inklusive “State machines og inputstyret bevægelse”, kan læses gratis i deres fulde længde her på webstedet. Derefter låser CoddyKit PRO alle lektioner op samt interaktive øvelser med en indbygget kodeeditor og en AI-underviser døgnet rundt. Mobiludvikling med Flutter-kurset indeholder 4 lektioner i alt.
Hvad lærer jeg i “State machines og inputstyret bevægelse”?
Forbind input fra Rive-state machines med appdata for reaktive animationer med forgreninger. Du øver dig i Mobiludvikling med Flutter med praktisk kode, som du kører direkte i browseren, og en AI-vejleder døgnet rundt besvarer dine spørgsmål, mens du arbejder dig gennem lektionen.
Skal jeg have erfaring for at begynde på Mobiludvikling med Flutter?
Der kræves ingen tidligere erfaring. Mobiludvikling med Flutter på CoddyKit er tilrettelagt for både begyndere og øvede, så du kan starte her eller fra begyndelsen og lære i dit eget tempo. Dette er lektion 2 af 4.
Hvor lang tid tager lektionen “State machines og inputstyret bevægelse”?
De fleste CoddyKit-lektioner tager cirka 5–10 minutter. Hver lektion er kort og interaktiv, så du gør løbende fremskridt og kan fortsætte, hvor du slap – på både web og app.
Kan jeg skrive og køre kode i denne Mobiludvikling med Flutter-lektion?
Ja. Alle Mobiludvikling med Flutter-lektioner har en indbygget kodeeditor, så du kan skrive og køre rigtig kode direkte i din browser og få øjeblikkelig feedback fra AI – uden lokal opsætning.
Alle lektioner i dette kursus
- Indlejring af Rive-assets og controllere
- State machines og inputstyret bevægelse
- Hero-overgange og bevægelse af delte elementer
- Forskudte og koreograferede AnimationControllers