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

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');
}

Related

Provide function context in dart

So i need to run some code that needs to get some data later.
I imagine it like:
voin runWithContext(void Function () fn, dynamic context) {
// ... magic
fn();
}
And later in the call stack of fn()
void otherFn() {
dynamic context = getContext();
}
If functions are not async we could just store the context as global variable, but the main requirement to support dart:async
I was looking into dart:mirrors and stacktrace but i can't find no way to bind some data.
You can do this with Zones.
Zones, and their "zone variables" are the canonical way to create a context that is preserved throughout an asynchronous computation).
Since you want single global function to access the context, it will have to be untyped (or typed at the one type that you need, but not generally reusable). That would me me want to reconsider the design, but if it works for you, I'd do it as:
import "dart:async";
/// Container for a mutable value.
class _Context {
dynamic value;
}
final _Context _rootContext = _Context();
R runWithNewContext<R>(R Function() body) =>
runZoned(body, zoneValues: {
#_context: _Context()..value = context,
});
dynamic get context => (Zone.current[#_context] ?? _rootContext).value;
set context(dynamic value) {
(Zone.current[#_context] ?? _rootContext).value = value;
}
If you don't need the mutability, you can simplify things a little, but not much.
The typed and unmodifable alternative is something like
class _Box { // Distinguishable from `null`, even if value is `null`
final Object? value;
_Box(this.value);
}
class ZoneStorage<T> {
final _Box _initialValue;
ZoneStorage(T initialValue) : _initialValue = _Box(initialValue);
R update<R>(T newValue, R Function() body) =>
runZoned(body, zoneValues: {this: _Box(newValue)});
T get value =>
(Zone.current[this] as _Box? ?? _initialValue).value as T;
}
That allows you to create multiple independent zone stores, like:
var zs1 = ZoneStorage<int>(1);
var zs2 = ZoneStorage<String>("yup");
zs1.update(42, () {
print(zs1.value);
print(zs2.value);
});
zs2.update("yup yup yup", () {
print(zs1.value);
print(zs2.value);
});
So the thing that is need is https://api.dart.dev/stable/2.17.3/dart-async/Zone-class.html
Hope that answer will help someone else

Dart Abstract class of Generic Type with Named Constructor

I am attempting to construct an abstract class that requires a named constructor in Dart. Given some Map (m), this generic type must be able instantiate itself.
The Dart compiler is throwing T.fromJson -> Invalid constructor name.
My attempt at coding this:
abstract class JsonMap<T> {
Map toJson();
T.fromJson(Map m);
}
I struggled with the same concept (in the same place ... API parsing :)) ) and I didn't found a proper solution.
But maybe you can use something this thing I found while checking block pattern this (I am not using it for my model part):
abstract class SomeBase {
void load();
}
class Provider<T extends SomeBase> extends InheritedWidget {
final T something;
Provider({
Key key,
#required this.something,
}): super(key: key);
#override
bool updateShouldNotify(_) {
return true;
}
static Type _typeOf<T>() => T;
static T of<T extends SomeBase>(BuildContext context){
final type = _typeOf<Provider<T>>();
Provider<T> provider = context.inheritFromWidgetOfExactType(type);
return provider.something;
}
}
OR just use this without encapsulating it in an inherited widget and provide the already initialised objects (like user or whatever you are parsing) that just load the values from the JSON provided.
You're creating a class named JsonMap that is parameterized on type T. T is not the name of your class, so T.fromJson is not a valid named constructor for JsonMap.
If you want JsonMap to have a named constructor, it should be JsonMap.fromJson(Map m).
Untested, but off the top of my head, you should write your code like so:
abstract class JsonMap<T> {
Map<String, dynamic> toJson();
T fromJson(Map<String, dynamic> m);
}
The dot makes fromJson(Map m) a constructor of type T, or a static function belonging to type T. Without the dot, it is a function belonging to the abstract class JsonMap, returning type T. Specifying the map type is good practice if you know what it will be (like with json).

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();

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.

Get BuildContext when not available through parameter

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.

Resources