View behind Scaffold - Flutter/Dart - flutter

I tried a lot to get the behavior of the iOS project https://github.com/ivanvorobei/SPLarkController working in Flutter / Dart. I do not understand how to get another view behind the scaffold (holding also the bottom navigation bar). Any ideas how this can be achieved?

This could be achieved with the help of Stack.
First layer for the buttons on the bottom:
Second layer for the main content:
Then, you can wrap the BottomNavBar inside GestureDetector with onVerticalDragUpdate property.
Complete Code:
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Builder(
builder: (context) => MyChild(MediaQuery.of(context).size.height),
),
),
);
}
}
class MyChild extends StatefulWidget {
final double screenHeight;
const MyChild(this.screenHeight, {Key? key}) : super(key: key);
#override
_MyChildState createState() => _MyChildState();
}
class _MyChildState extends State<MyChild> {
double val = 1.0;
#override
Widget build(BuildContext context) {
return Stack(
children: [
Container(
padding: const EdgeInsets.only(bottom: 20.0),
color: const Color(0xFF303030),
child: Padding(
padding: const EdgeInsets.only(left: 20.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Row(
children: [
ElevatedButton(
onPressed: () {}, child: const Text('Button 1')),
const SizedBox(
width: 20.0,
),
ElevatedButton(
onPressed: () {}, child: const Text('Button 2'))
],
),
const SizedBox(
height: 20,
),
Row(
children: [
ElevatedButton(
onPressed: () {}, child: const Text('Button 3')),
const SizedBox(
width: 20.0,
),
ElevatedButton(
onPressed: () {}, child: const Text('Button 4'))
],
),
],
),
),
),
LayoutBuilder(
builder: (context, constraints) => AnimatedContainer(
duration: const Duration(milliseconds: 500),
curve: Curves.ease,
height: constraints.maxHeight * val,
color: Colors.white,
child: Column(
children: [
Expanded(
child: ListView.builder(
physics: const BouncingScrollPhysics(),
itemCount: 25,
itemBuilder: (context, index) => ListTile(
title: Text('ListTile $index'),
),
),
),
GestureDetector(
onVerticalDragUpdate: (details) {
if (details.delta.dy < 0) { // If the user drags upwards
setState(() {
val = 0.7;
});
} else if (details.delta.dy > 0) { // If the user drags downwards
setState(() {
val = 1.0;
});
}
},
// Create your bottom navigation bar here
// and not bottomNavigationBar property of Scaffold
child: Container(
color: Colors.green.shade100,
height: 80,
),
)
],
),
),
),
],
);
}
}

Related

How can I position a FloatingActionButton spanning two widget in Flutter?

I trying to position a FloatingActionButton spanning two widget in flutter.
like this.
However, this is my app screen.
I can't stack the button on the center of two widget.
How can I solve my question :(? This is the sample code of my app. Thanks.
import 'package:flutter/material.dart';
class UserProfileBody extends StatelessWidget {
const UserProfileBody({
Key? key,
}) : super(key: key);
Widget _buildUserProfilePhoto(BuildContext context) {
return Container(
constraints: BoxConstraints(
maxHeight: MediaQuery.of(context).size.width,
),
width: MediaQuery.of(context).size.width,
child: const Image(
fit: BoxFit.contain,
image: NetworkImage(defaultUserProfileImage),
),
);
}
#override
Widget build(BuildContext context) {
return Column(
children: [
_buildUserProfilePhoto(context),
Stack(
children: [
Align(
alignment: Alignment.topRight,
child: FloatingActionButton(
onPressed: () {},
child: const Icon(Icons.photo_camera),
),
),
Column(
children: [
ListTile(
title: const Text('the title'),
subtitle: const Text('the subtitle'),
onTap: () {},
)
],
),
],
),
],
);
}
}
You can place image inside stack, in this case UI will be
class UserProfileBody extends StatelessWidget {
const UserProfileBody({
Key? key,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return LayoutBuilder(builder: (context, constraints) {
final double imageHeight = constraints.maxHeight * .5;
debugPrint(imageHeight.toString());
return Column(
children: [
Stack(
clipBehavior: Clip.none,
children: [
// image
Align(
alignment: Alignment.topCenter,
child: Container(
height: imageHeight,
color: Colors.green,
)
// _buildUserProfilePhoto(context),
),
Positioned(
top: imageHeight - (64 / 2), //64 is default fab size
right: 24,
child: FloatingActionButton(
onPressed: () {},
child: const Icon(Icons.photo_camera),
),
),
// Column(
// children: [
// ListTile(
// title: const Text('the title'),
// subtitle: const Text('the subtitle'),
// onTap: () {},
// )
// ],
// )
],
),
],
);
});
}
}
Play with
Positioned(
top: imageHeight - (64 / 2), //64 is default fab size
right: 24,
child: FloatingActionButton(
Solution 2:
And if you like place image above stack
Positioned(
top: -(64 / 2),
class UserProfileBody extends StatelessWidget {
const UserProfileBody({
Key? key,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return LayoutBuilder(builder: (context, constraints) {
final double imageHeight = constraints.maxHeight * .5;
debugPrint(imageHeight.toString());
return Column(
children: [
Container(
height: imageHeight,
color: Colors.green,
),
SizedBox(
height: constraints.maxHeight - imageHeight,
child: Stack(
clipBehavior: Clip.none,
children: [
Positioned(
top: -(64 / 2), //64 is default fab size
right: 24,
child: FloatingActionButton(
onPressed: () {},
child: const Icon(Icons.photo_camera),
),
),
Column(
children: [
ListTile(
title: const Text('the title'),
subtitle: const Text('the subtitle'),
onTap: () {},
)
],
)
],
),
),
],
);
});
}
}
Use clipBehavior: Clip.none, on stack

Flutter Navigation shows a black 404 screen when going back from a page

I'm facing this issue with Navigator in Flutter where when I go back from a page I get redirected to a black 404 page.
This is the navigation flow:
Login screen
HomePage screen
After being logged in, I am redirected to the Homepage and I shouldn't be able to go back to a previous screen, but if I swipe to the right I'm shown a black page with a 404 label and I can't move from there. Thank you in advance for any help!
Here I provide some code:
class SignInPage extends StatelessWidget {
static const routeName = '/signIn';
const SignInPage({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
Expanded(
child: Stack(
children: [
const Positioned(
left: -20,
top: -20,
child: AnimatedCircle(color: AppColors.lightBlue),
),
const Positioned(
right: -50, top: 150, child: AnimatedCircle(color: AppColors.lightOrange)),
const Positioned(
bottom: 60, left: -70, child: AnimatedCircle(color: AppColors.lightGreen)),
const Positioned(
right: -50, bottom: -60, child: AnimatedCircle(color: AppColors.lightRed)),
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.asset('assets/images/logo.png'),
Container(
width: MediaQuery.of(context).size.width * 8,
margin: const EdgeInsets.symmetric(horizontal: Dimens.SPACING_XL),
child: StyledButton.outlined(
title: 'Sign in with Google',
leading: Image.asset('assets/images/google.png', scale: 30),
onPressed: () async {
final res = await Authentication.signInWithGoogle();
await Authentication.userSetup(res.user!);
Navigator.of(context).popAndPushNamed(HomePage.routeName);
},
),
),
],
),
],
),
),
],
),
);
}
}
class HomePage extends StatelessWidget {
static const routeName = '/homepage';
static const _padding = EdgeInsets.symmetric(horizontal: 20.0, vertical: 6.0);
static const _dividerOptions = 30.0;
const HomePage({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (_) => ItemsProvider(),
child: Consumer<ItemsProvider>(
builder: (_, provider, __) => Scaffold(
resizeToAvoidBottomInset: false,
appBar: AppBar(
automaticallyImplyLeading: false,
actions: [
IconButton(
onPressed: () => Navigator.of(context).pushNamed('/user'),
icon: const Icon(Icons.person)),
],
),
floatingActionButton: Visibility(
visible: provider.buttonVisibility,
child: FloatingActionButton(
heroTag: "btn1",
backgroundColor: AppColors.red,
child: const Icon(Icons.add, color: AppColors.splashColor),
onPressed: () {
showModalBottomSheet(
context: context,
isScrollControlled: true,
builder: (context) => buildModal(context, provider),
);
}),
),
body: Padding(
padding: _padding,
child: Column(
children: [
TopCard(
itemCount: provider.itemsCount,
onPressed: () {
provider.deleteAllItem();
Navigator.of(context).pop();
},
),
const Divider(
height: _dividerOptions,
thickness: 1.0,
indent: _dividerOptions,
endIndent: _dividerOptions,
),
const Expanded(
child: ItemsList(),
),
],
),
),
),
),
);
}
SingleChildScrollView buildModal(
BuildContext context,
ItemsProvider provider,
) {
return SingleChildScrollView(
padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
child: Container(
decoration: const BoxDecoration(),
child: AddItemScreen(args: AddItemScreenArguments(provider.addItem)),
),
);
}
}

video playing in background flutter

im working through a scrollable page view somewhat like tic tok and each time i leave the screen the video continuously play in the background, which i dont want, cuz this code ids from a youtube tutorial i dont know how to work around the problem, so please help me check problem for a viable solution
import 'package:app/packageManager/package.dart';
import 'dart:io';
class Reels extends StatefulWidget {
const Reels({Key? key}) : super(key: key);
#override
State<Reels> createState() => _ReelsState();
}
vidPicker(ImageSource src, BuildContext context) async{
final vid = await ImagePicker().pickVideo(source: src);
if(vid != null){
Navigator.of(context).push(MaterialPageRoute(
builder: (context) => VideoUploader(
videoPath: vid.path, videoName:vid.name,
videoFile:File(vid.path)
)));
}
} //To Pick a Video
showDialogueBox(BuildContext, context){
return showDialog(context: context, builder: (context) => SimpleDialog(
children: [
SimpleDialogOption(
onPressed: () => vidPicker(ImageSource.gallery, context),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
children: const [
Icon(Icons.image),
Text(" Gallery ")
],
),
),
),
SimpleDialogOption(
onPressed: () => Navigator.of(context).pop(),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
children: const [
Icon(Icons.cancel),
Text(" Cancel ")
],
),
),
)
],
));
} //TO Show Option Like Gallery and Cancel
class _ReelsState extends State<Reels> {
#override
Widget build(BuildContext context) {
return Scaffold(
extendBodyBehindAppBar: true,
appBar: AppBar(
automaticallyImplyLeading: false,
elevation: 0,
flexibleSpace: SizedBox(
height: 200,
child: Padding(
padding: const EdgeInsets.all(15.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
IconButton(onPressed: () => showDialogueBox(BuildContext,context),
icon: const Icon(Icons.add),
iconSize: 35,
color: Colors.teal[300],
)
],
),
),
),
backgroundColor: Colors.transparent,
),
body: StreamBuilder(
stream: fireStore.collection("videoReels").snapshots(),
builder: (context, AsyncSnapshot<QuerySnapshot> snapshot) {
// TODO: Put Progress Bar Here
if (!snapshot.hasData) return const Text("Loading...");
return PageView.builder(
itemCount: snapshot.data!.docs.length,
scrollDirection: Axis.vertical,
controller: PageController(viewportFraction: 1, initialPage: 0),
itemBuilder: (context, index){
DocumentSnapshot dataSnapshot = snapshot.data!.docs[index];
return Stack(
alignment: Alignment.bottomCenter,
children: [
VideoPlayerContent(videoUrl: dataSnapshot["videoUrl"]),
Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Expanded(
flex: 3,
child: Container(
height: MediaQuery.of(context).size.height/6,
padding: const EdgeInsets.all(10.0),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Row(
children: [
InkWell(
onTap: () => {},
child: const Icon(Icons.thumb_up, color: Colors.blueAccent,),
),
const SizedBox(width: 30,),
InkWell(
onTap: () => {},
child: const Icon(Icons.favorite, color: Colors.red,),
)
],
),
const SizedBox(width: 20,),
InkWell(
onTap: () => {},
child: Text(" Add Comment here... ", style: TextStyle( color: Colors.grey[500] ),),
)
],
),
const SizedBox(height: 30,),
// Second Row
Padding(
padding: const EdgeInsets.fromLTRB(30.0, 4.0,30.0,4.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
InkWell(
onTap: () => { } ,
child:ClipRRect(
borderRadius: BorderRadius.circular(50.0),
child: CircleAvatar(backgroundImage: NetworkImage(dataSnapshot['avatar']), radius: 25,),
),
),
Text((dataSnapshot["videoUrl"])),
ElevatedButton(onPressed: ()=> {}, child: Text("Subscribe"))
],
),
)
],
),
),
),
// TODO: Remove this later
],
)
],
);
},
);
}
),
);
}
}
import 'package:app/packageManager/package.dart';
class VideoPlayerContent extends StatefulWidget {
final videoUrl;
const VideoPlayerContent({Key? key, required this.videoUrl}) : super(key: key);
#override
State<VideoPlayerContent> createState() => _VideoPlayerContentState();
}
class _VideoPlayerContentState extends State<VideoPlayerContent> {
late VideoPlayerController _videoController;
late Future _initializeVideoPlayer;
#override
void initState(){
_videoController = VideoPlayerController.network(widget.videoUrl);
_initializeVideoPlayer = _videoController.initialize();
_videoController.play();
_videoController.setVolume(1);
_videoController.setLooping(true);
super.initState();
}
#override
void dispose (){
_videoController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Container(
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
child: FutureBuilder(
future: _initializeVideoPlayer,
builder: (context, snapshot){
if (snapshot.connectionState == ConnectionState.done){
return VideoPlayer(_videoController);
}else{
return Container(
color: Colors.black,
child: const Center(
child: CircularProgressIndicator(
value: 0.8,
valueColor: AlwaysStoppedAnimation<Color>(Colors.purpleAccent),
),
)
);
}
},
),
);
}
}
Have you tried WidgetsBindingObserver and VisibilityDetector?
If you have not, then it might be handy to you.
For WidgetBindingObserver, simply use it as mixin like this:
class _VideoPlayerContentState extends State<VideoPlayerContent> with WidgetsBindingObserver{
#override
void initState(){
WidgetsBinding.instance?.addObserver(this);
super.initState();
}
#override
void didChangeAppLifecycleState(AppLifecycleState state) {
switch (state) {
case AppLifecycleState.resumed:
// TODO: Handle this case.
break;
case AppLifecycleState.inactive:
// Do like this in other lifecylestate if required !
_videoController.pause();
// TODO: Handle this case.
break;
case AppLifecycleState.paused:
// TODO: Handle this case.
break;
case AppLifecycleState.detached:
// TODO: Handle this case.
break;
}
super.didChangeAppLifecycleState(state);
}
// rest of the code .....
#override
void dispose(){
WidgetsBinding.instance?.removeObserver(this);
super.dispose();
}
}
Using visibility detector:
simply wrap the video player with this widget like:
VisibilityDetector(
// Must provide key
key: ValueKey<String>('give any string value to represent key'),
onVisibilityChanged: (visibilityInfo) {
// 0 ---> visible, 1 --> not visible
if(visibilityInfo.visibleFraction == 0){
_videoController.pause();
// might need setState over here
}
},
child : VideoPlayer(_videoController)
);

How to use the pageview inside show dialog (Flutter)?

The first section is an automatic page that I want to make in a dialog box with Flutter.
This is the code I used to try it out, but I couldn't get to what I wanted
Please help me with this by showing the pageView and auto indicator in the dialog
import 'package:flutter/material.dart';
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
#override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
final PageController _controller = PageController(initialPage: 0);
#override
void dispose() {
_controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: ElevatedButton(
child: const Text("Show Dialog"),
onPressed: () {
showDialog(
context: context,
builder: (BuildContext context) => AlertDialog(
title: const Text('Warning'),
content: PageView(
controller: _controller,
children: [
Container(width: double.infinity, height: double.infinity, color: Colors.yellow),
Container(width: double.infinity, height: double.infinity, color: Colors.red),
Container(width: double.infinity, height: double.infinity, color: Colors.black),
],
),
actionsAlignment: MainAxisAlignment.center,
actions: <Widget>[
Column(
children: [
ElevatedButton(onPressed: () {}, child: const Text("CONTINUE")),
OutlinedButton(onPressed: () {}, child: const Text("NO THANKS"))
],
),
],
),
);
},
),
),
);
}
}
We need to provide size on PageView. Based on your attached image, I am using LayoutBuilder to get the constraints and providing 30% height of the dialog. Use constraints to provide size.
showDialog(
context: context,
builder: (BuildContext context) =>
LayoutBuilder(builder: (context, constraints) {
debugPrint("${constraints.toString()}");
return AlertDialog(
title: const Text('Warning'),
content: Column(
children: [
SizedBox(
height: constraints.maxHeight * .3,
width: constraints.maxWidth,
child: PageView(
controller: _controller,
children: [
Container(color: Colors.yellow),
Container(color: Colors.red),
Container(color: Colors.black),
],
),
),
],
),
actionsAlignment: MainAxisAlignment.center,
actions: <Widget>[
Column(
children: [
ElevatedButton(
onPressed: () {},
child: const Text("CONTINUE")),
OutlinedButton(
onPressed: () {},
child: const Text("NO THANKS"))
],
),
],
);
}));
If you find the content get overflow after adding many widget wrap top column with SingleChildScrollView
showDialog(
context: context,
builder: (BuildContext context) =>
LayoutBuilder(builder: (context, constraints) {
debugPrint("${constraints.toString()}");
return AlertDialog(
title: const Text('Warning'),
content: SingleChildScrollView(
child: Column(
More about LayoutBuilder

Using Flow widget instead of BottomNavigationBar in Flutter

I'm trying to use Flow widget instead of BottomNavigationBar.
this is my code.
#override
Widget build(BuildContext context) {
final delegate = S.of(context);
return SafeArea(
child: Scaffold(
drawer: DrawerWidget(),
body: Stack(
children: [
_pages[_selectedPageIndex]['page'],
Positioned(
child: Container(
child: Flow(
delegate: FlowMenuDelegate(menuAnimation: menuAnimation),
children: menuItems
.map<Widget>((IconData icon) => flowMenuItem(icon))
.toList(),
),
),
),
]),
}
But after adding left, right, bottom, or top properties to the Positioned widget, the Flow widget gon.
You can copy paste run full code below
You can use ConstrainedBox and set Stack fit and Positioned with Container
SafeArea(
child: Scaffold(
body: ConstrainedBox(
constraints: BoxConstraints.expand(),
child: Stack(
alignment: Alignment.topLeft,
fit: StackFit.expand,
children: [
...
Positioned(
left: 0,
top: 0,
child: Container(
alignment: Alignment.topLeft,
width: MediaQuery.of(context).size.width,
height: 65,
child: FlowMenu()))
]),
),
),
);
working demo
full code
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: FlowTest(),
);
}
}
class FlowTest extends StatefulWidget {
#override
_FlowTestState createState() => _FlowTestState();
}
class _FlowTestState extends State<FlowTest> {
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
body: ConstrainedBox(
constraints: BoxConstraints.expand(),
child: Stack(
alignment: Alignment.topLeft,
fit: StackFit.expand,
children: [
ListView(
shrinkWrap: true,
children: <Widget>[
Column(
children: <Widget>[
SizedBox(height: 20.0),
ListView.builder(
shrinkWrap: true,
itemCount: 5,
physics: PageScrollPhysics(),
itemBuilder: (context, index) {
return Column(
children: <Widget>[
Container(
height: 50.0,
color: Colors.green,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Icon(Icons.format_list_numbered,
color: Colors.white),
Padding(
padding: const EdgeInsets.only(right: 5.0)),
Text(index.toString(),
style: TextStyle(
fontSize: 20.0, color: Colors.white)),
],
),
),
Container(
child: GridView.count(
crossAxisCount: 3,
shrinkWrap: true,
physics: PageScrollPhysics(),
childAspectRatio: 1.2,
children: List.generate(
8,
(index) {
return Container(
child: Card(
color: Colors.blue,
),
);
},
),
),
),
SizedBox(height: 20.0),
],
);
},
),
],
),
],
),
Positioned(
left: 0,
top: 0,
child: Container(
alignment: Alignment.topLeft,
width: MediaQuery.of(context).size.width,
height: 65,
child: FlowMenu()))
]),
),
),
);
}
}
class FlowMenu extends StatefulWidget {
#override
_FlowMenuState createState() => _FlowMenuState();
}
class _FlowMenuState extends State<FlowMenu>
with SingleTickerProviderStateMixin {
AnimationController menuAnimation;
IconData lastTapped = Icons.notifications;
final List<IconData> menuItems = <IconData>[
Icons.home,
Icons.new_releases,
Icons.notifications,
Icons.settings,
Icons.menu,
];
void _updateMenu(IconData icon) {
if (icon != Icons.menu) setState(() => lastTapped = icon);
}
#override
void initState() {
super.initState();
menuAnimation = AnimationController(
duration: const Duration(milliseconds: 250),
vsync: this,
);
}
Widget flowMenuItem(IconData icon) {
final double buttonDiameter =
MediaQuery.of(context).size.width / menuItems.length;
return Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: RawMaterialButton(
fillColor: lastTapped == icon ? Colors.amber[700] : Colors.blue,
splashColor: Colors.amber[100],
shape: CircleBorder(),
constraints: BoxConstraints.tight(Size(buttonDiameter, buttonDiameter)),
onPressed: () {
_updateMenu(icon);
menuAnimation.status == AnimationStatus.completed
? menuAnimation.reverse()
: menuAnimation.forward();
},
child: Icon(
icon,
color: Colors.white,
size: 45.0,
),
),
);
}
#override
Widget build(BuildContext context) {
return Container(
child: Flow(
delegate: FlowMenuDelegate(menuAnimation: menuAnimation),
children: menuItems
.map<Widget>((IconData icon) => flowMenuItem(icon))
.toList(),
),
);
}
}
class FlowMenuDelegate extends FlowDelegate {
FlowMenuDelegate({this.menuAnimation}) : super(repaint: menuAnimation);
final Animation<double> menuAnimation;
#override
bool shouldRepaint(FlowMenuDelegate oldDelegate) {
return menuAnimation != oldDelegate.menuAnimation;
}
#override
void paintChildren(FlowPaintingContext context) {
double dx = 0.0;
for (int i = 0; i < context.childCount; ++i) {
dx = context.getChildSize(i).width * i;
context.paintChild(
i,
transform: Matrix4.translationValues(
dx * menuAnimation.value,
0,
0,
),
);
}
}
}