Reputation: 147
I'm building a Flutter app importing the Rive package. I've found a set of animated icons which I implemented in the app's sidebar. I tried creating my animated icon (folder) by copying exactly the package downloaded "style". The sidebar is animated: when the user clicks on one of the elements of the list tile, it is highlighted. However, an error occurred. So, the error comes from my rive animation, not a part of my code, I suppose. If you're familiar with Rive I'm imploring you to help me because I'm getting crazy. Thank you
This is the error shown after I added my animation:
[VERBOSE-2:dart_vm_initializer.cc(41)] Unhandled Exception: type 'Null' is not a subtype of type 'SMIBool' in type cast
#0 _SideMenuState.build.<anonymous closure>.<anonymous closure>
side_menu.dart:56
#1 RiveAnimationState._init
rive_animation.dart:304
#2 RiveAnimationState._configure
rive_animation.dart:196
<asynchronous suspension>
Here is the code:
side_menu.dart
import 'package:flutter/material.dart';
import 'package:gym/models/rive_asset.dart';
import 'package:gym/utils/rive_utils.dart';
import 'package:gym/widgets/SideMenu/info_card.dart';
import 'package:gym/widgets/SideMenu/side_menu_tile.dart';
import 'package:rive/rive.dart';
class SideMenu extends StatefulWidget {
const SideMenu({super.key});
@override
State<SideMenu> createState() => _SideMenuState();
}
class _SideMenuState extends State<SideMenu> {
RiveAsset selectedMenu = sideMenu.first;
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
width: 288,
height: double.infinity,
color: Theme.of(context).colorScheme.primary.withOpacity(.5),
child: SafeArea(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const InfoCard(
name: 'name',
nomeUtente: 'user name',
),
Padding(
padding: const EdgeInsets.only(
left: 24,
top: 32,
bottom: 16,
),
child: Text(
'Browse',
style: Theme.of(context)
.textTheme
.titleMedium!
.copyWith(color: Colors.white70),
),
),
...sideMenu.map(
(menu) => SideMenuTile(
menu: menu,
riveonInit: (artboard) {
StateMachineController controller =
RiveUtils.getRiveController(
artboard,
stateMachineName: menu.stateMachineName,
);
menu.input = controller.findSMI('active') as SMIBool;
},
press: () {
menu.input!.change(true);
Future.delayed(
const Duration(milliseconds: 500),
() {
menu.input!.change(false);
},
);
setState(() {
selectedMenu = menu;
});
},
isActive: selectedMenu == menu,
),
),
Padding(
padding: const EdgeInsets.only(
left: 24,
top: 32,
bottom: 16,
),
child: Text(
'History',
style: Theme.of(context)
.textTheme
.titleMedium!
.copyWith(color: Colors.white70),
),
),
...sideMenu2.map((menu) => SideMenuTile(
menu: menu,
riveonInit: (artboard) {
StateMachineController controller =
RiveUtils.getRiveController(
artboard,
stateMachineName: menu.stateMachineName,
);
menu.input = controller.findSMI('active') as SMIBool;
},
press: () {
menu.input!.change(true);
Future.delayed(
const Duration(milliseconds: 500),
() {
menu.input!.change(false);
},
);
setState(() {
selectedMenu = menu;
});
menu.function(context);
},
isActive: selectedMenu == menu,
)),
const SizedBox(
height: 30,
),
const SizedBox(
height: 30,
width: 30,
child: RiveAnimation.asset(
'assets/RiveAssets/folder.riv',
fit: BoxFit.cover,
),
),
],
),
),
));
}
}
side_menu_tile.dart:
import 'package:flutter/material.dart';
import 'package:gym/models/rive_asset.dart';
import 'package:rive/rive.dart';
class SideMenuTile extends StatelessWidget {
const SideMenuTile({
super.key,
required this.menu,
required this.press,
required this.riveonInit,
required this.isActive,
});
final RiveAsset menu;
final VoidCallback press;
final ValueChanged<Artboard> riveonInit;
final bool isActive;
@override
Widget build(BuildContext context) {
return Column(
children: [
const Padding(
padding: EdgeInsets.only(left: 24),
child: Divider(
color: Colors.white24,
height: 1,
),
),
Stack(
children: [
AnimatedPositioned(
duration: const Duration(milliseconds: 300),
curve: Curves.fastOutSlowIn,
height: 56,
width: isActive ? 288 : 0,
left: 0,
child: Container(
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.primary.withOpacity(.3),
borderRadius: const BorderRadius.all(Radius.circular(10)),
),
),
),
ListTile(
onTap: press,
leading: SizedBox(
height: 34,
width: 34,
child: RiveAnimation.asset(
menu.src,
artboard: menu.artboard,
onInit: riveonInit,
),
),
title: Text(
menu.title,
style: const TextStyle(color: Colors.white),
),
),
],
),
],
);
}
}
model rive_asset.dart:
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:gym/screens/splash_screen.dart';
import 'package:rive/rive.dart';
class RiveAsset {
final String artboard, stateMachineName, title, src;
final Function function;
late SMIBool? input;
RiveAsset(
this.src, {
required this.artboard,
required this.stateMachineName,
required this.title,
required this.function,
this.input,
});
set setInput(SMIBool status) {
input = status;
}
}
List<RiveAsset> bottomNavs = [
RiveAsset(
'assets/RiveAssets/icons.riv',
artboard: 'CHAT',
stateMachineName: 'CHAT_Interactivity',
title: 'Chat',
function: () {},
),
RiveAsset(
'assets/RiveAssets/icons.riv',
artboard: 'SEARCH',
stateMachineName: 'SEARCH_Interactivity',
title: 'Search',
function: () {},
),
RiveAsset(
'assets/RiveAssets/icons.riv',
artboard: 'TIMER',
stateMachineName: 'TIMER_Interactivity',
title: 'Timer',
function: () {},
),
RiveAsset('assets/RiveAssets/icons.riv',
artboard: 'BELL',
stateMachineName: 'BEll_Interactivity',
title: 'Notifications',
function: () {}),
RiveAsset(
'assets/RiveAssets/icons.riv',
artboard: 'USER',
stateMachineName: 'USER_Interactivity',
title: 'Profile',
function: () {},
),
];
List<dynamic> sideMenu = [
// RiveAsset(
// 'assets/RiveAssets/icons.riv',
// artboard: 'HOME',
// stateMachineName: 'HOME_Interactivity',
// title: 'Home',
// function: (context) {},
// ),
RiveAsset(
'assets/RiveAssets/icons.riv',
artboard: 'USER',
stateMachineName: 'USER_Interactivity',
title: 'Profile',
function: (context) {},
),
RiveAsset(
'assets/RiveAssets/icons.riv',
artboard: 'SEARCH',
stateMachineName: 'SEARCH_Interactivity',
title: 'Search',
function: (context) {},
),
RiveAsset(
'assets/RiveAssets/icons.riv',
artboard: 'LIKE/STAR',
stateMachineName: 'STAR_Interactivity',
title: 'Favorites',
function: (context) {},
),
RiveAsset(
'assets/RiveAssets/icons.riv',
artboard: 'FOLDER',
stateMachineName: 'FOLDER_Interactivity',
title: 'Datas',
function: (context) {},
),
];
List<RiveAsset> sideMenu2 = [
RiveAsset(
'assets/RiveAssets/icons.riv',
artboard: 'CHAT',
stateMachineName: 'CHAT_Interactivity',
title: 'Help',
function: (context) {},
),
RiveAsset(
'assets/RiveAssets/icons.riv',
artboard: 'BELL',
stateMachineName: 'BELL_Interactivity',
title: 'Notifications',
function: (context) async {
await FirebaseAuth.instance.signOut();
Navigator.of(context).pushReplacement(MaterialPageRoute(
builder: (context) => const SplashScreen(),
)); //this function is here just to test the app during the login
},
),
];
rive_utils.dart:
import 'package:rive/rive.dart';
class RiveUtils {
static StateMachineController getRiveController(Artboard artboard,
{stateMachineName = 'State Machine 1'}) {
StateMachineController? controller =
StateMachineController.fromArtboard(artboard, stateMachineName);
artboard.addController(controller!);
return controller;
}
}
I also provide the .riv file to get the access to the animated icons: Rive Animations
If you need something I hadn't provided don't hesitate to ask.
Upvotes: 1
Views: 189
Reputation: 725
It seems that the problem is the cast, change it. I changed the line and it works fine for me. By the way I like the icons :)
change in side_menu.dart line 56:
menu.input = controller.findSMI('active') as SMIBool;
to:
menu.input = controller.findSMI<SMIBool>('active');
Upvotes: 0