Is someone able to explain to me why 'context' in there is undefined? i Watched like 5 videos about BuildContext and i still don't understand it. Yes i'm beginner with dart
class ZmienneClass extends ChangeNotifier {
void decrementCounter(int liczba) {
if (_rundy == 0) {
Navigator.push(context,
MaterialPageRoute(builder: (context) => resGamePage(title: "")));
};}}
The BuildContext is needed to tell Flutter where it should build its widgets. Currently you are trying to access a context without actually providing a valid BuildContext, if you really want to access the context, you could provide it trough the decrementCounter by passing one more parameter in it, decrementCounter(int liczba, BuildContext context) and pass the context from where you are calling it.
You dont declare BuildContext try below code refer BuildContext here
class ZmienneClass extends ChangeNotifier {
void decrementCounter(int liczba,BuildContext context) {
if (_rundy == 0) {
Navigator.push(context,
MaterialPageRoute(builder: (context) => resGamePage(title: "")));
};}}
Related
I'm trying to add flutter_bloc to my flutter application. I have Home page and when click on a button in home page Need to Navigation to a different page. Navigation is working without any issue, But When Wrap the the component with a BlocProvider getting below wrror
The argument type 'MyBloc' can't be assigned to the parameter type 'MyBloc Function(BuildContext)'
This what I have tried so far
My Bloc
import 'package:flutter_bloc/flutter_bloc.dart';
enum MyEvent {increment, decrement}
class MyBloc extends Bloc<MyEvent, int> {
MyBloc() : super(0);
#override
Stream<int> mapEventToState(MyEvent event) async* {
switch (event) {
case MyEvent.increment:
yield state + 1;
break;
case MyEvent.decrement:
yield state - 1;
break;
}
}
}
In home page I'm calling below function when click on button
Future<void> _routeToTodayTasks() {
return Navigator.push(
context,
MaterialPageRoute(
builder: (BuildContext context) => BlocProvider<MyBloc>(
create: MyBloc(),
child: TodayTasks(),
)));
}
Could anyone tell me what is wrong with my code?
As the error message clearly states, the create argument in BlocProvider is accepting a function with syntax MyBloc Function(BuildContext), but instead you are returning an object of your MyBloc class directly.
So, returns the bloc object from a function like this:
create: (context) => MyBloc(),
I have a MaterialButton with the following onPressed field:
onPressed: () async {
final int intResult = await showMyDialog(context, provider.myArg) ?? 0;
//...
}
Here is the showMyDialog function:
Future<int?> showMyDialog(BuildContext context, Object someArg) async {
return showDialog<int>(
context: context,
builder: (BuildContext context) {
return ChangeNotifierProvider<MyProvider>(
create: (_) => MyProvider(someArg),
child: MyDialog(),
);
},
);
}
Now the problem I see is that in MyDialog (a StatelessWidget), I need to use Navigator.pop(...) to return a value for the awaited intResult. However, I can't seem to find a way to strongly type these calls, and so it's hard to be certain that no runtime type error will happen.
The best I have right now, which is admittedly a bit inconvenient, is to subclass StatelessWidget and wrap the Navigator functions in it:
abstract class TypedStatelessWidget<T extends Object> extends StatelessWidget {
void pop(BuildContext context, [T? result]) {
Navigator.pop(context, result);
}
}
Then in a TypedStatelessWidget<int> we can use pop(context, 0) normally, and pop(context, 'hi') will be marked as a type error by the editor. This still doesn't link the dialog return type and the navigator, but at least it avoids manually typing each navigator call.
Is there any better way to have this strongly typed ?
You don't need to wrap the Navigator.pop method as you can type its return value.
Navigator.pop<int>(context, 1);
I let you refer to the pop method documentation for more details.
So here's what I ended up doing. Essentially, this allows for a widget type and a provider type to be "tied together". This way, when the custom pop function is called in the dialog, there is a type contract (see PopType) for what the return value should be. There is also support for non-manual pop navigation by using WillPopScope, which gets the typed provider's return function and arguments (see the abstract functions in TypedProvider) to properly pass that value up the widget tree.
mixin TypedWidget<T, N extends TypedProvider<T>?> on Widget {
ChangeNotifierProvider<N> asTypedWidget(
BuildContext context, N Function(BuildContext) createProviderFunc) {
return ChangeNotifierProvider<N>(
create: createProviderFunc,
builder: (BuildContext innerContext, _) {
return WillPopScope(
child: this,
onWillPop: () {
final N provider = Provider.of<N>(innerContext, listen: false);
if (provider == null) {
return Future<bool>.value(true);
}
provider.onPopFunction(provider.getPopFunctionArgs());
return Future<bool>.value(true);
});
});
}
/// Builds this TypedWidget inside `showDialog`.
///
/// The `TypedProvider`'s `onPopFunction` is called if the
/// dialog is closed outside of a manual `Navigator.pop()`. This doesn't have
/// a return type; all returning actions should be done inside the defined
/// `onPopFunction` of the provider.
///
/// Example:
/// ```
/// MyTypedWidget().showTypedDialog(
/// context,
/// (BuildContext context) => MyTypedProvider(...)
/// );
/// ```
Future<void> showTypedDialog(
BuildContext context, N Function(BuildContext) createProviderFunc,
{bool barrierDismissible = true}) async {
await showDialog<void>(
context: context,
barrierDismissible: barrierDismissible,
builder: (_) => asTypedWidget(context, createProviderFunc),
);
}
}
abstract class TypedProvider<PopType> with ChangeNotifier {
TypedProvider(this.onPopFunction);
Function(PopType) onPopFunction;
PopType getPopFunctionArgs();
void pop(BuildContext context) {
onPopFunction(getPopFunctionArgs());
Navigator.pop(context);
}
}
There are most likely other ways to achieve that, and this solution certainly has some constraints, but it effectively provides strong typing to the dialog.
I am new to Flutter and as I was reading through the tutorial I saw the following code snippet:
// Within the `FirstRoute` widget
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => SecondRoute()),
);
}
Why does the call in the MaterialPageRoute work? I see it requests an object of type WidgetBuilder, but what we pass is a BuildContext. Do the parentheses around context indicate a call to the constructor of the WidgetBuilder?
The builder parameter accepts an object of type WidgetBuilder as you say. What is "hidden" is that it is typedef:
typedef WidgetBuilder = Widget Function(BuildContext context);
So what you've passed is actually an anonymous function that match it.
It is called an arrow function. Actually this piece of code
(context) => SecondRoute()
can be rewritten like:
MaterialPageRoute(builder: (context) {
return SecondRoute();
})
And widget builder is a function, which MaterialPageRoute require as a parameter.
On some event, I want to navigate to another screen with Navigator.
I could easily achieve it with BlocListener:
BlocListener<BlocA, BlocAState>(
bloc: blocA,
listener: (context, state) {
if (state is Success) {
Navigator.of(context).pushNamed('/details');
}
},
child: Container(),
)
But I can't find the direct equivalent for it in a pure Provider.
The only way I see is to swap screens:
home: Consumer<Auth>(
builder: (_, auth, __) => auth.user == null ? LoginPage() : MainPage()
)
It's a common way. But it will not use Navigator, hence it will just 'pop' MainPage without screen transition.
On some event, I want to play some animation in UI.
I found in the documentation that Listenable class is intended for dealing with Animations, but it's not explained in details.
On some event, I want to clear a TextEditingController.
On some event, I want to show a dialog.
And more similar tasks...
How to solve it? Thanks in advance!
After some research I found a way. I'm not sure if it's the only or the best way, or the way foreseen by Provider's creator, however it works.
The idea is to keep a helper Stream inside of my Store class (I mean business-logic class provided with Provider), and to subscribe to its changes in my widget.
So in my Store class I have:
final _eventStream = StreamController.broadcast();
Stream get eventStream => _eventStream.stream;
void dispose() {
_eventStream.close();
super.dispose();
}
I add events to this stream inside of actions:
void navigateToNextScreen() {
_eventStream.sink.add('nav');
}
void openDialog() {
_eventStream.sink.add('dialog');
}
In my UI widget I have:
#override
void afterFirstLayout(BuildContext context) {
context.read<Transactions>().eventStream.listen((event) {
if (event == 'nav') {
Navigator.push(
context,
MaterialPageRoute(
builder: (ctx) => SecondScreen(),
),
);
} else if (event == 'dialog') {
showDialog(
context: context,
builder: (context) => AlertDialog(content: Text("Meow")));
}
});
}
I used here afterFirstLayout lifecycle method from the after_layout package, which is just a wrapper for WidgetsBinding.instance.addPostFrameCallback
07.07.20 UPD.: Just found a package that can be used for event reactions:
https://pub.dev/packages/event_bus
It basically uses the same approach with StreamController under the hood.
I tried passing data from a filter page to the home page, but keep getting the following error.
Error message on console - NoSuchMethodError being caused by widget within MaterialPageRoute()
//Radio button values to select user's gender on Filter Page
enum PrayditatorGender { Female, Male }
PrayditatorGender pGender;
//Radio button values to select Prayditation category on Filter Page
enum PrayditationFilter {
All,
Family,
Fellowship,
GodlyWisdom,
GoodSuccess,
HealthAndSafety,
}
PrayditationFilter pFilter = PrayditationFilter.All;
//Code to push the data from Filter Page to Home Page
Navigator.push(context, MaterialPageRoute(
builder: (context) {
PrayditatorHomePage(
pGender: pGender,
pFilter: pFilter
)
));
//Code to handle the data on Home Page
class PrayditatorHomePage extends StatefulWidget {
final PrayditatorGender pGender;
final PrayditationFilter pFilter;
PrayditatorHomePage({this.pGender, this.pFilter});
#override
_PrayditatorHomePageState createState() => _PrayditatorHomePageState();
}
class _PrayditatorHomePageState extends State<PrayditatorHomePage> {
#override
Widget build(BuildContext context) {}
Your syntax is wrong, you're not supposed to be having this issue, this code worked with no issues:
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => PrayditatorHomePage(
pGender: pGender,
pFilter: pFilter,
),
),
);
Thank you all for taking time to view/comment. Bug has been busted and code working effectively!
Syntax was all correct, however, a static parameter was inappropriately put in the place meant for a dynamic parameter. After all, lessons learned.