I am new to Rive and because there is close to no good documentation for Rive 2 I wanted to ask here. How do I play my Rive Animation in flutter? I copy + pasted the example that was on pub.dev for the rive dependecy and switched out their animation name to mine, but it just shows me a weird freezeframe of my animation. This is the code:
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:rive/rive.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> with SingleTickerProviderStateMixin {
#override
Widget build(BuildContext context) {
return const MaterialApp(
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key key}) : super(key: key);
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
void _togglePlay() {
setState(() => _controller.isActive = !_controller.isActive);
}
/// Tracks if the animation is playing by whether controller is running.
bool get isPlaying => _controller?.isActive ?? false;
Artboard _riveArtboard;
RiveAnimationController _controller;
#override
void initState() {
super.initState();
// Load the animation file from the bundle, note that you could also
// download this. The RiveFile just expects a list of bytes.
rootBundle.load('assets/file.riv').then(
(data) async {
// Load the RiveFile from the binary data.
final file = RiveFile.import(data);
// The artboard is the root of the animation and gets drawn in the
// Rive widget.
final artboard = file.mainArtboard;
// Add a controller to play back a known animation on the main/default
// artboard.We store a reference to it so we can toggle playback.
artboard.addController(_controller = SimpleAnimation('idle'));
setState(() => _riveArtboard = artboard);
},
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: _riveArtboard == null
? const SizedBox()
: Rive(artboard: _riveArtboard),
),
floatingActionButton: FloatingActionButton(
onPressed: _togglePlay,
tooltip: isPlaying ? 'Pause' : 'Play',
child: Icon(
isPlaying ? Icons.pause : Icons.play_arrow,
),
),
);
}
}
You might need to change the name of the animation you want to play from your Rive file.
This line:
artboard.addController(_controller = SimpleAnimation('idle'));
attempts to play an animation called 'idle'. If you animation is named differently, try replacing the name here.
This blog post has more info on using Rive with Flutter.
final url = Uri.parse(
'https://raw.githubusercontent.com/rive-app/rive-flutter/master/example/assets/liquid_download.riv');
final res = await http.get(url);
final file = RiveFile.import(res.bodyBytes.buffer.asByteData());
// rootBundle.load('assets/file.riv')
Related
I want to use a rive animation like this one
https://rive.app/community/1514-2958-flower-composition-tutorial/
I notice that this artboard contains a somes NestedArtboard this nested artboard are not loaded by my app, I would like to know why this happen?
This is my code:
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:rive/rive.dart';
class RiveBackground extends StatefulWidget {
const RiveBackground({Key? key}) : super(key: key);
#override
State<RiveBackground> createState() => _RiveBackgroundState();
}
class _RiveBackgroundState extends State<RiveBackground> {
// Declarations necessary to rive
final riveFileName = 'assets/rive/last.riv';
Artboard? globalArtboard;
// Animation controller
late RiveAnimationController _animationController;
// Loads a Rive file
Future<void> _loadRiveFile() async {
final bytes = await rootBundle.load(riveFileName);
RiveFile rFile = RiveFile.import(bytes);
final artboard = rFile.artboardByName('Motion');
print(globalArtboard);
globalArtboard = artboard!
..addController(
_animationController = SimpleAnimation('Animation 1'),
);
setState(() {});
}
#override
void initState() {
WidgetsBinding.instance!.addPostFrameCallback((_) => _loadRiveFile());
super.initState();
}
#override
Widget build(BuildContext context) {
print('Building');
return Scaffold(
body: globalArtboard != null
? Container(
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
child: Rive(
fit: BoxFit.cover,
artboard: globalArtboard!,
),
)
: const Center(child: Text('empty')),
);
}
}
Expected result
My result
Not sure if you found a solution for this already, but according to this response in this Github issue,
to get the nested artboard to show through, you should instance the Artboard when you set it up with the Rive widget, so your setup would go from:
Rive(artboard: riveFile.artboardByName("artboard"));
to
Rive(artboard: riveFile.artboardByName("artboard")!.instance());
more detail in the link. I hope this helps!
I am using GetX. I need to listen changes in TextController. The follow code do not work:
class Controller extends GetxController{
final txtList = TextEditingController().obs;
#override
void onInit() {
debounce(txtList, (_) {
print("debouce$_");
}, time: Duration(seconds: 1));
super.onInit();
}
}
Is does not print nothing when I am changing txtList value from UI. I suppose it's because it does not check text field inside txtList.
How to get it work?
You need to pass an RxInterface into debounce to do this via GetX. Just create an RxString and add a listener to the controller then pass the RxString into debounce.
class Controller extends GetxController {
final txtList = TextEditingController();
RxString controllerText = ''.obs;
#override
void onInit() {
txtList.addListener(() {
controllerText.value = txtList.text;
});
debounce(controllerText, (_) {
print("debouce$_");
}, time: Duration(seconds: 1));
super.onInit();
}
}
Then on any page in the app you can pass in that controller into the textfield and it'll print the value after the user stops typing for 1 second.
class Home extends StatelessWidget {
final controller = Get.put(Controller());
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: TextField(controller: controller.txtList), // this will print
),
);
}
}
And if you need that value for anything else it's also always accessible via controller.controllerText.value.
By TextEditingController.text, we can already get changing text input value so it does not need .obs.
To pass parameter for debounce, we should pass value itself : txtList.text. (see here: https://github.com/jonataslaw/getx/blob/master/documentation/en_US/state_management.md)
final txtList = TextEditingController(); // 1. here
#override
void onInit() {
debounce(txtList.text, (_) { // 2. here
print("debouce$_");
}, time: Duration(seconds: 1));
super.onInit();
}
This might work.
=================== added 11/21 ==================
Here's the example. I know the RxString variable seems a duplication for TextEditingController.text, but GetX's debounce function needs RxString type variable as a parameter. I tried to find more elegant way to do this, but I couldn't find anything. Please let me know if somebody knows a better way.
// in controller
late final TextEditingController textController;
final RxString userInput = "".obs;
#override
void onInit() {
super.onInit();
textController = TextEditingController();
userInput.value = textController.text;
textController.addListener(() {
userInput.value = textController.text;
}
);
debounce(userInput, (_) {
print("debouce$_");
}, time: Duration(seconds: 1));
}
check this snippet for example to listen to TextEditingController text change listener
import 'package:flutter/material.dart';
void main() async {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(),
darkTheme: ThemeData.dark(),
home: const HomePage(),
);
}
}
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
#override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
final TextEditingController controller = TextEditingController();
#override
void initState() {
super.initState();
controller.addListener(_printLatestValue);
}
void _printLatestValue() {
print('Second text field: ${controller.text}');
}
#override
void dispose() {
controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: TextField(
controller: controller,
),
);
}
}
I'm trying to use ObjectBox as the database in a flutter application. The following is the sample code.
However, while execution I was returned with the error of "_store is not initialized".
class _HomePageState extends State<HomePage> {
...
// 👇 ADD THIS
late Stream<List<ShopOrder>> _stream;
#override
void initState() {
super.initState();
setNewCustomer();
getApplicationDocumentsDirectory().then((dir) {
_store = Store(
getObjectBoxModel(),
directory: join(dir.path, 'objectbox'),
);
setState(() {
// 👇 ADD THIS
_stream = _store
.box<ShopOrder>()
// The simplest possible query that just gets ALL the data out of the Box
.query()
.watch(triggerImmediately: true)
// Watching the query produces a Stream<Query<ShopOrder>>
// To get the actual data inside a List<ShopOrder>, we need to call find() on the query
.map((query) => query.find());
hasBeenInitialized = true;
});
});
}
...
}```
initialize the databases in the main one and then you pass the store to the HomePage, that is why it tells you that error '_store no se inicializa'. You must declare your global store and then you pass it to each view.
late Store _stores;
void main() async {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
#override
_MyState createState() => _MyState();
}
class _MyState extends State<MyApp> {
bool iniciando_store = true;
#override
void initState() {
super.initState();
initPlatformState();
getApplicationDocumentsDirectory().then((directory) {
_stores = Store(
getObjectBoxModel(),
directory: join(directory.path, 'objectbox')
);
setState(() {
iniciando_store = false;
});
});
}
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(create: (context) => ThemeProvider()),
],
child: Consumer<ThemeProvider>(builder: (context, theme, snapshot) {
return MaterialApp(
title: 'Object box title',
home: !iniciando_store
? MyHomePage(
title: "Home", loadingSore: iniciando_store, STORE: _stores)
: MyStatefulWidget());
}),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage(
{Key? key,
required this.title,
required this.loadingSore,
required this.STORE})
: super(key: key);
final String title;
final Store STORE;
final bool loadingSore;
#override
_MyHomePageState createState() => _MyHomePageState();
}
this is the simple way to connect with Object box
I am a newbie , and I am facing a problem beyond my little knowledge.
I am making a test project, where I want to use a splashscreen, as a newbie, I used flutter_native_splash to create splash screen; it is working fine, but now I want to hold the splash screen and show an AlertDialog about internet connectivity.
I don't know how to use it :(
extra Q. is there any way to use an button to the AlertDialog, which will reopen / resume the process if the internet connection restore?
Try this package data_connection_checker
class InitialScreen extends StatefullWidget {
#override
_InitialScreenState createState() => _InitialScreenState();
}
class _InitialScreenState extends State<InitialScreen> {
bool hasConnection;
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) async {
setState(() {
hasConnection = await DataConnectionChecker().hasConnection;
});
});
}
#override
Widget build(BuildContext) {
if(hasConnection) {
return Text('hasConnection');
} else {
return Text('Connection error');
}
}
}
Hours of researching with noob knowledge, I maybe found a solution of my problem -->
import 'dart:async';
import 'dart:io';
import 'package:flutter/material.dart';
class SplashScreen extends StatefulWidget {
SplashScreen({Key key}) : super(key: key);
#override
_SplashScreenState createState() => _SplashScreenState();
}
class _SplashScreenState extends State<SplashScreen> {
int internetValue = 0;
Future<bool> asyncNetCheck() async {
final result = await InternetAddress.lookup('google.com');
if (result.isNotEmpty && result[0].rawAddress.isNotEmpty) {
return Future<bool>.value(true);
}
}
#override
void initState () {
super.initState();
new Future.delayed(const Duration(seconds: 3),(){
setState(() {
internetValue = 2;
asyncNetCheck().then((value) {
internetValue = 1;
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (context) => HomePage()),
);
});
});
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: GlobalColor.splashColorFill,
body: Container(
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage("assets/splash.png"),
),
),
child: internetValue == 0 ? Container(): internetValue == 2? Container(child: Center(child: Text("checking Internet..."),),) : Container()
)
);
}
}
but still there is a problem though, if internet connection is slow or the process is slowing the thread; the "checking internet..." text splash for a few moment, hope I can fix that too!and to my extra question, I will place an button in space of text, and will call the initstate, hope that will cover at least minimum area!
I have problem with assest audio player package when I try to play two songs inside one page
both are playing !
The way I want when I press first button,first song play and when I press second button the first song stop and the second song start playing .
I used this code but it doesn't work
HomePage
import 'package:flutter/material.dart';
import 'package:mp3player/playpausebutton.dart';
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Mp3 Player'),
),
body: Container(
color: Colors.white,
child: Column(
children: [
PlayPauseButton(
mp3name: 'song1',
),
PlayPauseButton(
mp3name: 'song2',
)
],
),
),
);
}
}
PlayPauseButton
class PlayPauseButton extends StatefulWidget {
PlayPauseButton({this.mp3name});
final String mp3name;
#override
_PlayPauseButtonState createState() => _PlayPauseButtonState();
}
class _PlayPauseButtonState extends State<PlayPauseButton> {
final assetsAudioPlayer = AssetsAudioPlayer();
bool ispresed = false;
#override
void dispose() {
// TODO: implement dispose
super.dispose();
assetsAudioPlayer.dispose();
}
#override
Widget build(BuildContext context) {
return FlatButton(
child: Icon(ispresed ? Icons.pause : Icons.play_arrow),
onPressed: () {
assetsAudioPlayer.open(Audio("assets/audios/${widget.mp3name}.mp3"));
setState(() {
if (ispresed == false) {
assetsAudioPlayer.play();
ispresed = true;
} else if (ispresed == false) {
assetsAudioPlayer.pause();
ispresed = false;
}
});
},
);
}
}
I used this package for playing audio
https://pub.dev/packages/assets_audio_player
and also is there any way to toggle button Icon when player is finish ?
My problem is solved by changing
final assetsAudioPlayer = AssetsAudioPlayer();
to
final assetsAudioPlayer = AssetsAudioPlayer.withId("0");
you can check if the player is done playing by adding listener to it.
assetsAudioPlayer.playlistAudioFinished.listen((event){if(event) {//carry out another action you want } });
the callback response is a bool type, return false when the audio start and return true when it finished