Reduce motion: the source
Open the live demo · Read the guide · View on GitHub
Regions the guide quotes: value (line 18), read (line 60), compare (line 84).
1/// What the player has already told the operating system, read as a default
2/// for a camera's own involuntary movement.
3///
4/// **`flutter3d_game`'s real `Accommodations` is not a dependency of this
5/// app.** It is nine lines wrapping `MediaQuery.maybeDisableAnimationsOf`;
6/// this page reimplements exactly that shape and calls the same real
7/// Flutter API, so the numbers below answer to this device's actual
8/// reduce-motion setting, not a stand-in for it.
9///
10/// Quoted by `accommodations.md` and shown whole in the Source tab.
11library;
13import 'package:flutter/widgets.dart';
14import 'package:flutter3d/flutter3d.dart';
15import 'package:flutter3d_showcase/src/demo/demo.dart';
16import 'package:vector_math/vector_math.dart';
18/// A default, never an override: a game reads this where the player has not
19/// said otherwise, and a slider they moved themselves still wins.
20final class _Accommodations {
21 const _Accommodations({this.reduceMotion = false});
23 factory _Accommodations.of(BuildContext context) => _Accommodations(
24 reduceMotion: MediaQuery.maybeDisableAnimationsOf(context) ?? false,
25 );
27 final bool reduceMotion;
29 /// Nought when the player has asked for less movement: a camera that
30 /// shakes on every landing is exactly what the system setting is about.
31 double get cameraMotion => reduceMotion ? 0.0 : 1.0;
33 /// The same fallback for a full-screen flash, kept separate because a
34 /// flash is a photosensitivity question and a moving camera is a
35 /// vestibular one.
36 double get screenFlash => reduceMotion ? 0.0 : 1.0;
37}
39final class AccommodationsDemo extends ShowcaseDemo {
41 Scene build(DemoContext context) {
42 final material = Material(
43 name: 'ball',
44 baseColor: Vector4(0.8, 0.6, 0.4, 1.0),
45 );
46 final node = MeshNode(
47 DeviceMesh.upload(context.device, SphereShape(segments: 16).build()),
48 material,
49 );
50 return Scene()
51 ..add(node)
52 ..add(
53 LightNode(name: 'sun', intensity: 3.0)
54 ..setLocalForward(Vector3(-0.4, -1.0, -0.3)),
55 );
56 }
59 Widget? customBody(BuildContext buildContext, DemoContext context) {
60 final accommodations = _Accommodations.of(buildContext);
61 return Container(
62 color: const Color(0xFF14161A),
63 padding: const EdgeInsets.all(24),
64 alignment: Alignment.topLeft,
65 child: DefaultTextStyle(
66 style: const TextStyle(color: Color(0xFFE8E8EC), fontSize: 16),
67 child: Text(
68 'this device asks for reduced motion: '
69 '${accommodations.reduceMotion}\n'
70 'a camera rig would default its own motion to '
71 '${accommodations.cameraMotion}\n'
72 'a full-screen flash would default to '
73 '${accommodations.screenFlash}',
74 ),
75 ),
76 );
77 }
80 void verify(Scene scene, FrameResult frame) {
81 if (frame.drawCalls < 1) {
82 throw StateError('the ball was not drawn');
83 }
84 const off = _Accommodations();
85 const on = _Accommodations(reduceMotion: true);
86 if (off.cameraMotion != 1.0 || on.cameraMotion != 0.0) {
87 throw StateError(
88 'reduced motion should zero the camera motion '
89 'default and nothing else should',
90 );
91 }
92 }
93}