I am trying to use Bloc Architecture to set the Color state of child widgets, according to the documentation, and it just doesn't work. Here are my relevant files:

blocProvider.dart

import 'package:flutter/material.dart';

Type _typeOf<T>() => T;

abstract class BlocBase {
  void dispose();
}

class BlocProvider<T extends BlocBase> extends StatefulWidget {
  BlocProvider({
    Key key,
    @required this.child,
    @required this.bloc,
  }) : super(key: key);

  final Widget child;
  final T bloc;

  @override
  _BlocProviderState<T> createState() => _BlocProviderState<T>();

  static T of<T extends BlocBase>(BuildContext context) {
    final type = _typeOf<_BlocProviderInherited<T>>();
    _BlocProviderInherited<T> provider =
        context.ancestorInheritedElementForWidgetOfExactType(type)?.widget;
    return provider?.bloc;
  }
}

class _BlocProviderState<T extends BlocBase> extends State<BlocProvider<T>> {
  @override
  void dispose() {
    widget.bloc?.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return new _BlocProviderInherited<T>(
      bloc: widget.bloc,
      child: widget.child,
    );
  }
}

class _BlocProviderInherited<T> extends InheritedWidget {
  _BlocProviderInherited({
    Key key,
    @required Widget child,
    @required this.bloc,
  }) : super(key: key, child: child);

  final T bloc;

  @override
  bool updateShouldNotify(_BlocProviderInherited oldWidget) => false;
}

colorBloc.dart

import 'dart:async';
import 'package:ultimate_mtg/model/blocprovider.dart';
import 'dart:ui';
import 'dart:math';


class ColorBloc extends BlocBase {
// streams of Color
  StreamController streamListController = StreamController<Color>.broadcast();
// sink
  Sink get colorSink => streamListController.sink;
// stream
  Stream<Color> get colorStream => streamListController.stream;

// function to change the color
  changeColor() {
    colorSink.add(getRandomColor());
  }

  @override
  dispose() {
    streamListController.close();
  }
}



// Random Colour generator
Color getRandomColor() {
  Random _random = Random();
  return Color.fromARGB(
    _random.nextInt(256),
    _random.nextInt(256),
    _random.nextInt(256),
    _random.nextInt(256),
  );
}

And my child widget:

dropdownmenu.dart

import 'package:flutter/material.dart';
import 'dart:math';
import 'package:ultimate_mtg/model/colorBloc.dart';
import 'package:ultimate_mtg/model/blocprovider.dart';

// ignore: camel_case_types
class dropDownMenu extends StatefulWidget {
  final Function() onPressed;
  final String tooltip;
  final IconData icon;
  final _callback;

  dropDownMenu({Key key, this.onPressed, this.tooltip, this.icon, @required void singlePlayerCallbacks(String callBackType), @required StatefulWidget styleMenu }  ):
      _callback = singlePlayerCallbacks;

  @override
  dropDownMenuState createState() => dropDownMenuState();
}

// ignore: camel_case_types
class dropDownMenuState extends State<dropDownMenu>
    with SingleTickerProviderStateMixin {
  bool isOpened = false;
  AnimationController _animationController;
  Animation<double> _translateButton;
  Curve _curve = Curves.easeOut;
  double _fabHeight = 58;
  double menuButtonSize = 55;
  Color menuButtonTheme;
  ColorBloc colorBloc = ColorBloc();

  @override
  initState() {
    _animationController =
    AnimationController(vsync: this, duration: Duration(milliseconds: 600))
      ..addListener(() {
        setState(() {});
      });
    _translateButton = Tween<double>(
      begin: 0.0,
      end: _fabHeight,
    ).animate(CurvedAnimation(
      parent: _animationController,
      curve: Interval(
        0.0,
        1.0,
        curve: _curve,
      ),
    ));
    super.initState();
  }

  @override
  dispose() {
    _animationController.dispose();
    super.dispose();
  }

  animate() {
    if (!isOpened) {
      _animationController.forward();
    } else {
      _animationController.reverse();
    }
    isOpened = !isOpened;
  }

  Widget backgroundColour() {
    colorBloc = BlocProvider.of(context);
    return StreamBuilder(
      initialData: Colors.blue,
      stream: colorBloc.colorStream,
      builder: (BuildContext context, snapShot) => Container(
        width: menuButtonSize,
        height: menuButtonSize,
        child: RawMaterialButton(
          shape: CircleBorder(),
          fillColor: Colors.black,
          elevation: 5.0,
          onPressed: (){},
          child: Container(
            height: menuButtonSize - 3,
            width: menuButtonSize - 3,
            decoration: BoxDecoration(
              color: snapShot.data,
              shape: BoxShape.circle,
            ),
            child: Image.asset(
              'lib/images/background_colour.png',
              scale: 4,
            ),
          ),
        ),
      ),
    );
  }

  Widget toggle() {
    return Transform.rotate(
      angle: _animationController.value * (pi * 2),
      child: Container(
        width: menuButtonSize,
        height: menuButtonSize,
        child: RawMaterialButton(
          shape: CircleBorder(),
          fillColor: Colors.black,
          elevation: 5.0,
          onPressed: animate,
          child: SizedBox(
            height: menuButtonSize - 3,
            width: menuButtonSize - 3,
            child: Image.asset('lib/images/ic_launcher.png'),
          ),
        ),
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Stack(
      children: <Widget> [
        BlocProvider(
          bloc: ColorBloc(),
          child: Column(
            mainAxisAlignment: MainAxisAlignment.start,
            children: [
              Stack(
                children: <Widget>[
                  Transform(
                    transform: Matrix4.translationValues(
                      0,
                      _translateButton.value,
                      0,
                    ),
                    child: backgroundColour(),
                  ),
                  toggle(),
                ],
              ),
            ],
          ),
        ),
        Column(
          mainAxisAlignment: MainAxisAlignment.start,
          children: <Widget>[
            Container(
              height: menuButtonSize,
              width: menuButtonSize,
              child: Opacity(
                opacity: 0.0,
                child: FloatingActionButton(
                  heroTag: null,
                  onPressed: animate,
                ),
              ),
            ),
            SizedBox(
              height: 3.0,
            ),
            Container(
              height: menuButtonSize,
              width: menuButtonSize,
              child: Opacity(
                opacity: 0.0,
                child: FloatingActionButton(
                  heroTag: null,
                  onPressed:  isOpened == true? (){
                    widget?._callback('background');
                  } : () {},
                ),
              ),
            ),
          ],
        ),
      ],
    );
  }
}

The error I am getting is in the backgroundColour() widget:

  Widget backgroundColour() {
    colorBloc = BlocProvider.of(context);
    return StreamBuilder(
      initialData: Colors.blue,
      stream: colorBloc.colorStream,

On the line that says > stream: colorBloc.colorStream,

And here is the specific error in the logs:

I/flutter (18865): ══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
I/flutter (18865): The following NoSuchMethodError was thrown building dropDownMenu(dirty, state:
I/flutter (18865): dropDownMenuState#7edb3(ticker inactive)):
I/flutter (18865): The getter 'colorStream' was called on null.
I/flutter (18865): Receiver: null
I/flutter (18865): Tried calling: colorStream

I have followed the documentation as closely as I can, but I can't see anywhere they they initialise this stream, or anything of that nature. I am really not understanding this error or how to resolve it. Anyone have any experience with streams?

1 Answers

1
nonybrighto On Best Solutions

You didn't pass in your BlocBase subclass when calling your 'of' method.

use colorBloc = BlocProvider.of<ColorBloc>(context);
instead of
colorBloc = BlocProvider.of(context);

EDIT:
Looking at the code again, You don't need that line since ColorBloc resides in the current class and not up the widget tree. Just delete that line to make use of the ColorBloc instance above.