Francesco Zanoncelli
Francesco Zanoncelli

Reputation: 147

_TypeError (type 'Null' is not a subtype of type 'SMIBool' in type cast)

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

Answers (1)

Franklin Diaz
Franklin Diaz

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');

enter image description here

video demo app animated icon

Upvotes: 0

Related Questions