Get BuildContext when not available through parameter - dart

Is there an alternative to Navigator.push which doesn't require a context? Here is my problem.
I have a very nested and deep callstack and not each function has a BuildContext parameter. Is there any other way to get the current BuildContext than through passing it from one function to another?
test1.dart
Widget build(BuildContext) {
...
new GestureDetector(
onTap: () => foo1() // very deep callstack until it calls foo30
...
}
test2.dart
void foo30() {
// need context here like:
BuildContext context = IHopeThatExists.getCurrentContext();
// why do I need that? Navigator.push needs a context or is there an alternative? FYI: I don't need to go back to the previous page though
Navigator.push(context, ..)
}

You can use the Builder widget:
return Builder(
builder: (context) {
return RaisedButton(
onPressed: () {
Navigator.of(context).push(...);
},
...
);
},
);

If your call stack is that deep you might want to consider refactoring =D. But that aside, if you use a StatefulWidget it has a context property which is the same as what is passed to the build function.
And FYI - from the State.build docs:
The BuildContext argument is always the same as the context property
of this State object and will remain the same for the lifetime of this
object. The BuildContext argument is provided redundantly here so that
this method matches the signature for a WidgetBuilder.

Related

Riverpod 2.0: override with value does not exist anymore

I have a provider named gameControllerProvider extends with ChangeNotifier, I initialize it first throw UnimplementedError.
final gameControllerProvider =
ChangeNotifierProvider<GameController>((ref) => throw UnimplementedError());
In my GameBodyWidget I ovverridewith value my gameControlllerProvider
class GameBodyWidget extends ConsumerWidget {
/// {#macro game_body_screen}
const GameBodyWidget({Key? key, required this.gameLevel}) : super(key: key);
final GameLevel gameLevel;
#override
Widget build(BuildContext context, WidgetRef ref) {
return ProviderScope(
overrides: [
gameControllerProvider.overrideWithValue(
GameController()..initializeController(gameLevel, ref),
),
],
child: Stack(
...
),
);
}
As you can see, the reason why I override the controller provider because I need to initialize and pass the parameter of WidgetRef and the GameLevel instance.
Yeah I know I can use the .family method but the problem there is that when I need to call this provider I always pass the parameter
ref.watch(gameControllerProvider('exampleParameter'));
As the changelog says, overrideWithValue is temporarily removed.
But to begin with, in the code you shared, you shouldn't be using it.
Instead do:
ProviderScope(
overrides: [
gameControllerProvider.overrideWithProvider(
ChangeNotifierProvider((ref) {
return GameController()..initializeController(gameLevel, ref),
}),
),
],
)
Using overrideWithValue here is unsafe as a ChangeNotifier is a stateful object. If the widget somehow rebuilt, your ChangeNotifier would be recreated and your state would be lost.
Using overrideWithProvider does not have this issue, as the provider will cache the ChangeNotifier.
For now you can use overrideWithProvider.
This change is temporary, and these methods will be reintroduced in a later version.

returning lists in one dart file to another dart file

I am using lists to create inkwell buttons. I want to put the lists in a separate dart file and import the file into the files where I use the lists. I don't know how to import the lists.
https://pastebin.com/mf0kvsGu
I made a dart file to put the lists into.
https://prnt.sc/ndu736 "Lists error"
void _loginPressed() {
// these handlers are called whenever the user tries to login, resend password or create an account
print('The use wants to login with $_email and $_password');
//if (_email == ""&& _password == "") {
ButtonsLists();
Navigator.push(
context,
MaterialPageRoute(
builder: (BuildContext context) => BrowsePage(buttonList)));
//}
}
class BuyItem extends StatelessWidget {
BuyItem(this.name, this.image);
final String name;
final String image;
#override
Widget build(BuildContext context) {
ButtonsLists();
return InkWell(
child: Card(
child: GridTile(
......
http://prntscr.com/ndu7rc "Import error"
The code works if I don't have the lists in a separate file and instead have them where the ButtonsLists(); is at.
That's because import will only import you the class written in the file you're referring to. More precisely, The ability to create instances of the class in the imported Dart file
To solve your problem
1- Initialize your lists globally inside ButtonsLists class. (before the Build() method). I'd rather to declare them final
2- Fill your lists with data as you like in the Build() method
3- Create getters to get a copy of your filled lists
4- Create an instance of the ButtonsLists class inside the _LoginPageState class and call the getter of any list whenever you need
The implementation should be something like this:
class ButtonsLists extends StatelessWidget {
final List<MainButtons> _buttonList = new List();
#override
Widget build(BuildContext context) {
//Build your lists here
}
List<MainButtons> getMainButtonsList(){
return _buttonList;
}
Then inside _LoginPageState class create an instance of the class ButtonsLists like this:
ButtonsLists mLists = new ButtonsLists();
and call your list anywhere in the class like this:
mLists.getMainButtonsList();

How to fake a context when there's no widget?

Here's my situation.
Let us say I have a Widget W1, with context C1. I also have a generic class like so:
class Klass {
static void doSomething(BuildContext context) {
Navigator.push(context, builder: (context) => W2());
}
}
I am calling doSomething in W1 as follows:
Klass.doSomething(C1);
Now, when I do a Navigator.pop(context) in W2, I am getting
Tried calling: ancestorStateOfType(Instance of
'TypeMatcher')
Now, I know with Builder() we can fake a context, but when it is a non-widget, what are my options?
Builder doesn't make a "fake" context – it's the real deal.
Similarly, you cannot fake a context, not can you obtain one outside of the widget tree.
The definition of BuildContext is that it represents the location of a Widget in the widget tree. Therefore it doesn't make to fake it, as you'll end up faking the widget tree itself.
Instead, you should refactor your method such that it doesn't need a BuildContext.
For example instead of:
void foo(BuildContext context) {
Navigator.of(context).pushNamed('/foo');
}
we can have:
void foo(NavigatorState navigatorState) {
navigatorState.pushNamed('/foo');
}

Fat Arrow notation with curly braces in setState Dart/Flutter

I am very new to Dart/Flutter and I have a confusion regarding the => notation. The documentation says that the => notation is used as a shorthand to return a single expression.
bool isNoble(int atomicNumber) => _nobleGases[atomicNumber] != null;
My doubt comes when I am trying to set state in a flutter application.
RaisedButton(
onPressed: () => {
setState(() {
print('hello');
_products.add('More stuff');
})
},
child: Text('Add Product'),
),
Now when i change the setState method with => notation
RaisedButton(
onPressed: () => {
setState(() => {
print('hello'),
_products.add('More stuff'),
})
},
child: Text('Add Product'),
),
Both methods mentioned above work, that is they set the state as expected. All i had to do was change the semicolons to commas when using the fat arrow notation.
What is the logic behind this ? How is the fat arrow notation working with curly braces which contains multiple expressions within it.
Edit
As mentioned by Hemanth Raj the => returns a set and the code segment containing the => notation can be written as follows.
RaisedButton(
onPressed: () => {
setState(() {
return {
print('hello'),
_products.add('More stuff'),
};
})
},
child: Text('Add Product'),
),
How is the returned set containing a print function and _products.add actually updating the state. Shouldn't it throw some kind of error because usually setState is done by an expression such as _products.add('More stuff');.
This is one of the interesting questions that I would love to answer.
As the official documents say here, yes => is used as a shorthand syntax to { return ... } which means => will just return whatever is produced on the righthand side.
Also from Dart 2.2 and above, a Set can be defined with comma separated values enclosed in a {} as mentioned in docs here.
Hence, the syntax you are using, i.e {} with statements separated with a comma, it is treated as a Set by the => functions. Each element being a function call, () => { f(a) , f(b), g(a),} would return a Set with the elements returned by each function call.
This example might help you understand what is happening under the hood:
dynamic reflect(dynamic a){
return a;
}
void main() {
Function shortHand = () => {reflect(1),reflect('a'),reflect({}),reflect([]),}; // this function when called will return a Set<dynamic>
print(shortHand().runtimeType); // will print `_LinkedHashSet<dynamic>`
}
So the syntax
() => '...' returns a String,
() => [ ... , ..., ...] returns a List
and similarly () => { ... , ... , ... } actually returns a Set
Note: This method of returning set with comma separated function calls is not recommended, would request you also not to use it unless you wanted a Set to be returned as result
Reply to the Edit :
Let me breakdown the function call and results for you. So your code goes like this,
() => {
setState(() {
return {
print('hello'),
_products.add('More stuff'),
};
})
}
Here the => returns a Set with the result of setState, i.e it'll return { (result of setState call) } which might be { null }
As you have call setState the below code gets executed, which again returns a Set with { (result of print), (result of _product.add), }
() {
return {
print('hello'),
_products.add('More stuff'),
};
}
State will update, as you are executing _products.add('More stuff'), where 'More stuff' will be added to _products irrespective of where you call it. When setState is being called, the widget will be rebuilt with the _products with new data added.
Hope this helped!
For the record, the recommended syntax for what you are doing is:
RaisedButton(
onPressed: () {
setState(() {
print('hello');
_products.add('More stuff');
});
},
child: Text('Add Product'),
),
The syntax (args) => { statements } is not how Dart writes function bodies, you do either (args) { statements } or (args) => singleExpression.
Also, you need to terminate statements with semicolons, ;, not commas.
As others have pointed out, the syntax you use (args) => { print("something"), somethingElse } is actually creating a set (a Set<void> because the return type of print is void) and returning that.
This is a perfect storm of small syntax mistakes, which would seem reasonable to a JavaScript programmer, that comes together to actually mean something completely different in Dart.
And, just to make things even worse, the code works. The set literal will evaluate its expression in order, and nobody sees the created set anyway. The syntax just doesn't generalize — you can't change any of the expressions to, say, a for-loop (yet, you will be able to in the next version of Dart).
So, in Dart, never use => { unless you want to return a set or map.

The argument type '_PostCreateRoute' can't be assigned to the parameter type 'Route<Map>

I am trying to make a social networking site using flutter . This is a feed related page which stores if there are feed available for the user or not and if there are feed it shows it . I am using asynchronous method for action during feed update so while creating on-pressed it is showing an error while creating PostCreateRoute object that the argument type '_PostCreateRoute' can't be assigned to the parameter type 'Route'. and I am new to flutter and if someone can help me out it would be great .
class _FeedActionButtonState extends State<FeedActionButton> {
BuildContext _context;
#override
Widget build(BuildContext context) {
_context = context;
return new FloatingActionButton(
child: new Icon(Icons.add, color:Theme.of(context).iconTheme.color),
onPressed: onPressed,
backgroundColor: Colors.white);
}
onPressed() async{
Mappost=await
Navigator.of(_context).push(new_PostCreateRoute(_context));
if (post!=null &&widget.onPosted!=null){
this .widget.onPosted(post)
}
}
}
It looks like you are passing _PostCreateRoute() as an argument to the push function which only accepts the arguments of type Route().
You first need to wrap your class in a route object like MaterialPageRoute (Android) or CupertinoPageRoute (iOS).
You can try something like:
Navigator.push(_context, MaterialPageRoute(builder: (BuildContext _context) => _PostCreateRoute(_context)));
Make sure you import material.dart or cupertino.dart depending on which route type you go with.

Resources