Cannot find StreamZip or StreamGroup - flutter

I cannot use StreamGroup or StreamZip for some reason
here's the code (that is complete useless, but I don't want to anger the admins)
import 'dart:async';
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
const MyApp() : super(key: const Key('MyApp'));
#override
Widget build(BuildContext context) => const MaterialApp(home: MyHomePage());
}
class MyHomePage extends StatefulWidget {
const MyHomePage() : super(key: const Key('MyHomePage'));
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
static const _duration = Duration(seconds: 3);
static const _initialCx = 3.0;
final _pageStream = Stream<int>.periodic(_duration, (int i) => i);
ScrollController _scrollController;
StreamController<double> _throttle;
#override
void initState() {
_scrollController = ScrollController();
_throttle = StreamController();
StreamGroup<num>.merge([_pageStream, _throttle.stream]); // <= this
StreamZip([_pageStream, _throttle.stream]); // or this
/// i NEED TO IMPLEMENT A LISTENER HERE... THE CODE IS USELESS OTHERWISE
super.initState();
}
#override
void dispose() {
_scrollController?.dispose();
_throttle?.close();
super.dispose();
}
#override
Widget build(BuildContext context) => Scaffold(
body: Stack(children: <Widget>[
ListView.builder(
controller: _scrollController,
itemBuilder: (BuildContext context, int index) => const Center(
child: FlutterLogo(size: 160),
),
),
Align(
alignment: Alignment.bottomCenter,
child: StreamBuilder<double>(
builder: (context, throttle) => Slider(
min: 1,
max: 5,
onChanged: (double value) => _throttle.sink.add(value),
value: throttle.data ?? _initialCx,
),
),
),
]),
);
}
here are the errors respectively for
StreamZip
The method 'StreamZip' isn't defined for the type '_MyHomePageState'.
Try correcting the name to the name of an existing method, or defining
a method named 'StreamZip'.
SteamGroup
The name 'StreamGroup' isn't a class. Try correcting the name to match
an existing class.
I admittedly don't have much experience with those classes, so the solution might be trivial,
can you please explain me how do I access those?
Thank you

It's not obvious due to the way that the Flutter API documentation merges everything together, but StreamZip and StreamGroup come from package:async, not from dart:async. You'll need to replace import 'dart:async'; with import 'package:async/async.dart'; and add an async dependency in your pubspec.yaml file.
(Even though package:async is from the Dart developers, it's separate from dart:async because:
The classes and functions in package:async aren't considered to be core parts of the Dart SDK.
package:async can be implemented in pure Dart code.
A separate package can be updated separately from the Dart SDK, allowing for more frequent updates.)

Related

Provider is not updating my ListView - Singleton

I can't figure why my widget ScrollViewStory won't update when CurrentStoryModel.instance changes.
Is there a problem with the fact that CurrentStoryModel is a singleton ?
Here are the widgets:
class Story extends StatelessWidget {
#override
Widget build(BuildContext _) {
return ChangeNotifierProvider<CurrentStoryModel>(
create: (_) => CurrentStoryModel.instance,
child: Expanded(
// fill the whole space
child: Container(
decoration: BoxDecoration(
border: Border.all(
color: Colors.blue,
width: 2,
),
),
child: ScrollViewStory(),
),
),
);
}
}
class ScrollViewStory extends StatefulWidget {
ScrollViewStory({Key? key}) : super(key: key);
#override
State<ScrollViewStory> createState() => _ScrollViewStoryState();
}
class _ScrollViewStoryState extends State<ScrollViewStory> {
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return Consumer<CurrentStoryModel>(
builder: (context, currentStoryModel, child) => ListView.builder(
itemCount: currentStoryModel.sections.length,
itemBuilder: (BuildContext context, int index) {
return new StorySection(section: currentStoryModel.sections[index]);
}),
);
}
}
And my model:
class CurrentStoryModel extends ChangeNotifier {
static final CurrentStoryModel _singleton = CurrentStoryModel._();
// Singleton accessor
static CurrentStoryModel get instance => _singleton;
List<SectionModel> _sections = [];
CurrentStoryModel._();
List<SectionModel> get sections => _sections;
// add new entry
void addSection(SectionModel newSection) {
_sections.add(newSection);
notifyListeners();
}
}
In another part of my code, I call addSection() and the new section is effectively added to the list. But my ListView won't update...
Any help appreciated !
Thanks.
I don't really know what to do...
It happens that for the ChangeNotifier to know that your list has changed, you should not add with .add because the list will remain the same. What you must do is to pass it a new instance.
Your code would look like this:
class CurrentStoryModel extends ChangeNotifier {
static final CurrentStoryModel _singleton = CurrentStoryModel._();
// Singleton accessor
static CurrentStoryModel get instance => _singleton;
List<SectionModel> _sections = [];
CurrentStoryModel._();
List<SectionModel> get sections => _sections;
// add new entry
void addSection(SectionModel newSection) {
_sections = [..._sections, newSection];
notifyListeners();
}
}
Sorry for my bad english
In fact, the widget was updating, but new data was below the screen boundaries.
All I needed to do was scrolling...

Giving error when using Provider state management in Flutter

I am trying to increase counter when app call init and using provider to change the state of counter but getting this error i don't understand why i have made the listen to false but no solved kindly help
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:providerpractice/counter.dart';
class HomeScreen extends StatefulWidget {
const HomeScreen({Key? key}) : super(key: key);
#override
_HomeScreenState createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
int count = 0;
Timer? timer;
#override
void initState() {
super.initState();
timer = Timer.periodic(Duration(seconds: 1), (timer) {
var pro = Provider.of<Counter>(context, listen: false);
pro.addCounter();
});
}
#override
Widget build(BuildContext context) {
print("build" + count.toString());
return Scaffold(
appBar: AppBar(
title: Text("Provider State Management"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Consumer<Counter>(builder: ((context, value, child) {
return Text(
value.counter.toString(),
style: TextStyle(fontSize: 30),
);
}))
],
),
),
);
}
}
If I am not mistaken, you can't use "context" in the initState method, rather move it out of there put it in a didChangeDependancy method.
I am sure someone can elaborate more on this, but the widget has no context until after the initState.
I think the error you're getting is a result of the fact that you do not have any logic to dispose of your active work. Try implementing something in onDispose().
You could also elaborate more on the error you're getting. There is no mention of the error type or statement in your question.

Flutter access Widget variable in another widget

I can not get it done to access a simple property of a stateless widget from another widget...
Class Month:
class Month {
final String name;
const Month(this.name);
}
MonthCardWidget:
class MonthCardWidget extends StatelessWidget {
final Month month;
const MonthCardWidget({Key key, this.month}) : super(key: key);
...
in my Stateful widget:
final List months = [
MonthCardWidget(month: Month('January')),
MonthCardWidget(month: Month('February')),
MonthCardWidget(month: Month('March')),
MonthCardWidget(month: Month('April')),
MonthCardWidget(month: Month('May')),
MonthCardWidget(month: Month('Juni')),
MonthCardWidget(month: Month('Juli')),
MonthCardWidget(month: Month('August')),
MonthCardWidget(month: Month('September')),
MonthCardWidget(month: Month('October')),
MonthCardWidget(month: Month('November')),
MonthCardWidget(month: Month('December')),
];
and I try to access the name like this:
months[index].month.name
but this is not working... What am I missing here?
Would it not be easier to make a list of Month models instead of widgets?
I think what you want is a state management solution. For this you can use packages like provider or GetX. My personal favorite is the GetX Package. You just need to create a class where you put your lists,variables,... you want to access globally and then call them with a controller.
For a detailed documentation check out their package on pub.dev
https://pub.dev/packages/get
Good Luck! :)
It's probably a bad idea to access the properties of the Widget from its ancestor. The data flows in the other direction. Using State Management is a great way to manage/share your data around your Widget Tree. Check this curated list of State Management Packages. My personal favorite is Riverpod (a new package from the same author as the Provider package).
For your particular case, I'm not even sure you need a Month class and a List<MonthCardWidget> months. Months are just numbers from 1 to 12.
The children of your wheel can be generated as a List:
ClickableListWheelScrollView(
[...]
child: ListWheelScrollView(
[...]
children: List.generate(12, (index) => MonthCardWidget(month: index + 1)),
),
);
And then, your MonthCardWidget just receives an int month and displays the month name thanks to the intl package, giving you i18n for free:
class MonthCardWidget extends StatelessWidget {
final int month;
const MonthCardWidget({Key key, this.month}) : super(key: key);
#override
Widget build(BuildContext context) {
return Container(
color: Colors.amber.shade300,
child: Center(
child: Text(DateFormat.MMMM().format(DateTime(2000, month))),
),
);
}
}
Full source code
import 'package:clickable_list_wheel_view/clickable_list_wheel_widget.dart';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
void main() {
runApp(
MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Calendar Wheel Demo',
home: HomePage(),
),
);
}
class HomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: MyListWheel(),
);
}
}
class MyListWheel extends StatefulWidget {
#override
_MyListWheelState createState() => _MyListWheelState();
}
class _MyListWheelState extends State {
final _scrollController = FixedExtentScrollController(initialItem: 11);
#override
Widget build(BuildContext context) {
final double _itemHeight = MediaQuery.of(context).size.height / 3;
return ClickableListWheelScrollView(
scrollController: _scrollController,
itemHeight: _itemHeight,
itemCount: 12,
scrollOnTap: true,
child: ListWheelScrollView(
controller: _scrollController,
itemExtent: _itemHeight,
physics: FixedExtentScrollPhysics(),
diameterRatio: 3,
squeeze: 0.95,
children:
List.generate(12, (index) => MonthCardWidget(month: index + 1)),
),
);
}
}
class MonthCardWidget extends StatelessWidget {
final int month;
const MonthCardWidget({Key key, this.month}) : super(key: key);
#override
Widget build(BuildContext context) {
return Container(
color: Colors.amber.shade300,
child: Center(
child: Text(DateFormat.MMMM().format(DateTime(2000, month))),
),
);
}
}

Flutter: Unhandled Exception: Bad state: Cannot add new events after calling close (NOT SAME CASE)

I am trying to use the BLoC pattern to manage data from an API and show them in my widget. I am able to fetch data from API and process it and show it, but I am using a bottom navigation bar and when I change tab and go to my previous tab, it returns this error:
Unhandled Exception: Bad state: Cannot add new events after calling close.
I know it is because I am closing the stream and then trying to add to it, but I do not know how to fix it because not disposing of the publish subject will result in a memory leak.
I know maybe this question is almost the same as this question.
But I have implemented it and it doesn't work in my case, so I make questions with a different code and hope someone can help me in solving my case. I hope you understand, Thanks.
Here is my BLoC code:
import '../resources/repository.dart';
import 'package:rxdart/rxdart.dart';
import '../models/meals_list.dart';
class MealsBloc {
final _repository = Repository();
final _mealsFetcher = PublishSubject<MealsList>();
Observable<MealsList> get allMeals => _mealsFetcher.stream;
fetchAllMeals(String mealsType) async {
MealsList mealsList = await _repository.fetchAllMeals(mealsType);
_mealsFetcher.sink.add(mealsList);
}
dispose() {
_mealsFetcher.close();
}
}
final bloc = MealsBloc();
Here is my UI code:
import 'package:flutter/material.dart';
import '../models/meals_list.dart';
import '../blocs/meals_list_bloc.dart';
import '../hero/hero_animation.dart';
import 'package:dicoding_submission/src/app.dart';
import 'detail_screen.dart';
class DesertScreen extends StatefulWidget {
#override
DesertState createState() => new DesertState();
}
class DesertState extends State<DesertScreen> {
#override
void initState() {
super.initState();
bloc.fetchAllMeals('Dessert');
}
#override
void dispose() {
bloc.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: getListDesert()
);
}
getListDesert() {
return Container(
color: Color.fromRGBO(58, 66, 86, 1.0),
child: Center(
child: StreamBuilder(
stream: bloc.allMeals,
builder: (context, AsyncSnapshot<MealsList> snapshot) {
if (snapshot.hasData) {
return _showListDessert(snapshot);
} else if (snapshot.hasError) {
return Text(snapshot.error.toString());
}
return Center(child: CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation<Color>(Colors.white)
));
},
),
),
);
}
Widget _showListDessert(AsyncSnapshot<MealsList> snapshot) => GridView.builder(
itemCount: snapshot == null ? 0 : snapshot.data.meals.length,
gridDelegate:
SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2),
itemBuilder: (BuildContext context, int index) {
return GestureDetector(
child: Card(
elevation: 2.0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(5))),
margin: EdgeInsets.all(10),
child: GridTile(
child: PhotoHero(
tag: snapshot.data.meals[index].strMeal,
onTap: () {
showSnackBar(context, snapshot.data.meals[index].strMeal);
Navigator.push(
context,
PageRouteBuilder(
transitionDuration: Duration(milliseconds: 777),
pageBuilder: (BuildContext context, Animation<double> animation,
Animation<double> secondaryAnimation) =>
DetailScreen(
idMeal: snapshot.data.meals[index].idMeal),
));
},
photo: snapshot.data.meals[index].strMealThumb,
),
footer: Container(
color: Colors.white70,
padding: EdgeInsets.all(5.0),
child: Text(
snapshot.data.meals[index].strMeal,
textAlign: TextAlign.center,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontWeight: FontWeight.bold, color: Colors.deepOrange),
),
),
),
),
);
},
);
}
If you need the full source code, this is the repo with branch submission-3
bloc.dispose(); is the problem.
Since the bloc is initialised outside your UI code, there is no need to dispose them.
Why are you instantiating your bloc on the bloc class?
You must add your bloc instance somewhere in your widget tree, making use of a InheritedWidget with some Provider logic. Then in your widgets down the tree you would take that instance and access its streams. That is why this whole process it is called 'lifting up the state'.
That way, your bloc will always be alive when you need it, and the dispose would still be called sometime.
A bloc provider for example:
import 'package:flutter/material.dart';
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 T bloc;
final Widget child;
#override
State<StatefulWidget> 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;
}
static Type _typeOf<T>() => T;
}
class _BlocProviderState<T extends BlocBase> extends State<BlocProvider<T>> {
#override
Widget build(BuildContext context) {
return new _BlocProviderInherited(
child: widget.child,
bloc: widget.bloc
);
}
#override
void dispose() {
widget.bloc?.dispose();
super.dispose();
}
}
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(InheritedWidget oldWidget) => false;
}
It makes use of a combination of InheritedWidget (to be available easily down the widget tree) and StatefulWidget (so it can be disposable).
Now you must add the provider of some bloc somewhere into your widget tree, that is up to you, I personally like to add it between the routes of my screens.
In the rout of my MaterialApp widget:
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'MyApp',
onGenerateRoute: _routes,
);
}
Route _routes(RouteSettings settings) {
if (settings.isInitialRoute)
return MaterialPageRoute(
builder: (context) {
final mealsbloc = MealsBloc();
mealsbloc.fetchAllMeals('Dessert');
final homePage = DesertScreen();
return BlocProvider<DesertScreen>(
bloc: mealsbloc,
child: homePage,
);
}
);
}
}
With the help of routes, the bloc was created 'above' our homePage. Here I can call wherever initialization methods on the bloc I want, like .fetchAllMeals('Dessert'), without the need to use a StatefulWidget and call it on initState.
Now obviously for this to work your blocs must implements the BlocBase class
class MealsBloc implements BlocBase {
final _repository = Repository();
final _mealsFetcher = PublishSubject<MealsList>();
Observable<MealsList> get allMeals => _mealsFetcher.stream;
fetchAllMeals(String mealsType) async {
MealsList mealsList = await _repository.fetchAllMeals(mealsType);
_mealsFetcher.sink.add(mealsList);
}
#override
dispose() {
_mealsFetcher.close();
}
}
Notice the override on dispose(), from now on, your blocs will dispose themselves, just make sure to close everything on this method.
A simple project with this approach here.
To end this, on the build method of your DesertScreen widget, get the available instance of the bloc like this:
var bloc = BlocProvider.of<MealsBloc>(context);
A simple project using this approach here.
For answers that resolve my problem, you can follow the following link: This
I hope you enjoy it!!

PageView throws '_positions.isNotEmpty': ScrollController not attached to any scroll views

I'm trying to change the active page index via a pagecontroller in Flutter, using the Bloc pattern and its throwing "'_positions.isNotEmpty': ScrollController not attached to any scroll views.".
This is my code:
WelcomeWizardScreen:
import 'package:flutter/widgets.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:fluttertest/blocs/wizard/bloc/bloc.dart';
import 'package:fluttertest/screens/wizardsteps/joincongregation.dart';
import 'package:fluttertest/screens/wizardsteps/welcometomapman.dart';
class WelcomeWizardScreen extends StatefulWidget {
#override
_WelcomeWizardScreenState createState() => _WelcomeWizardScreenState();
}
class _WelcomeWizardScreenState extends State<WelcomeWizardScreen> {
final WizardBloc wizardBloc = WizardBloc();
#override
Widget build(BuildContext context) {
// TODO: implement build
return BlocProvider(
builder: (BuildContext context) => WizardBloc(),
child: PageView(
children: <Widget>[WelcomeToMapMan(), JoinCongregation()],
controller: wizardBloc.pageController,
),
);
}
}
WizardBloc:
import 'package:bloc/bloc.dart';
import 'package:flutter/widgets.dart';
import 'wizard_state.dart';
import 'wizard_event.dart';
class WizardBloc extends Bloc<WizardEvent, WizardState> {
int activeStep = 0;
final PageController pageController = PageController(initialPage: 0, keepPage: false, viewportFraction: 0.4);
#override
WizardState get initialState => WelcomeToMapManState();
#override
Stream<WizardState> mapEventToState(
WizardEvent event,
) async* {
if (event is ChangePage)
{
pageController.jumpToPage(event.pageIndex);
}
// TODO: Add Logic
}
Stream<WizardState> _mapJoinCongregationToState() async* {
}
}
One of the screens in the PageView:
class JoinCongregation extends StatelessWidget {
#override
Widget build(BuildContext context) {
final WizardBloc _wizardBloc = BlocProvider.of<WizardBloc>(context);
// TODO: implement build
return Column(
children: <Widget>[
Center(
child: Text("this is step 2"),
),
RaisedButton(
child: Text("back to step 1"),
onPressed: () => {_wizardBloc.dispatch(ChangePage(0))},
)
],
);
}
}
It seems like the PageViewController isn't "attached" to the PageView when it is called on to change pages, but it initalises correctly (on the correct page index).
How can I solve this? I'm fairly new to flutter.
You shouldn't create a PageController in a bloc because a bloc should not be coupled with the UI (theoretically you should be able to reuse your bloc between Flutter and AngularDart). Please refer to https://github.com/felangel/bloc/issues/18 for an example of how you can accomplish this.