gbtb16/kiwi

unregister should not throw error if class does not exist

Closed this issue · 6 comments

I want my widget to register some class in kiwi and after widget is disposed to unregister that class. The problem I experience is following:

User opens page A;
page A register class B in kiwi;
User leaves page A;
User enters page A before dispose A is called;
User gets error for registering class in kiwi that is already registered;
dispose A gets called and class B gets unregistered;

Obvious solution is to call unregister always before register but that throws error if class doesn't exist.

Can you provide me with a working small working example where you have this problem?

import 'package:flutter/material.dart';
import 'package:kiwi/kiwi.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) => MaterialApp(
        home: FirstPage(),
      );
}

class FirstPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        leading: IconButton(
          icon: Icon(Icons.add),
          onPressed: () => Navigator.of(context).push(
            MaterialPageRoute(
              builder: (context) => ClassA(),
            ),
          ),
        ),
      ),
    );
  }
}

class ClassA extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => ClassAState();
}

class ClassAState extends State<ClassA> {
  @override
  void initState() {
    super.initState();
    print('init A called');
    KiwiContainer().registerInstance(ClassB());
  }

  @override
  void dispose() {
    print('dispose A called');
    KiwiContainer().unregister<ClassB>();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Container(
        color: Colors.red,
      ),
      appBar: AppBar(
        leading: IconButton(
          icon: Icon(Icons.arrow_back_ios),
          onPressed: () => Navigator.of(context).pop(),
        ),
      ),
    );
  }
}

class ClassB extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container(
      color: Colors.blue,
    );
  }
}

Fast tap on top left button while screens are changing will eventually create error.

So what I wanted to do is in Class A

void initState() {
    super.initState();
    print('init A called');
    **KiwiContainer().unregister<ClassB>();**
    KiwiContainer().registerInstance(ClassB());
  }

So that i ensure that class B is always unregistered before I register it, but this throws error.

The silent field on KiwiContainer will make sure no error is thrown:

  @override
  void initState() {
    super.initState();
    print('init A called');
    KiwiContainer().silent = true;
    KiwiContainer().unregister<ClassB>();
    KiwiContainer().silent = false;
    KiwiContainer().registerInstance(ClassB());
  }

  @override
  void dispose() {
    print('dispose A called');
    KiwiContainer().silent = true;
    KiwiContainer().unregister<ClassB>();
    KiwiContainer().silent = false;
    super.dispose();
  }

But maybe a better solution is just to register a factory. So every time you want to get ClassB a new ClassB is created? (This is perfect for view models because you only need to get your ViewModel once. and after that you just use your in memory ViewModel.)

#6 could also help. But that is currently not yet supported in the generator.

Silent field can do the trick then.