How to find a child widget of certain type by context in Flutter? - flutter

I have a Statefull/Stateless widget I need to check if the widget contains certain type of widget as its child.
Example:
Statefull Widget -> Container() => Column() => [Text(), Expanded() -> ListView()]
Here I need to check if the Statefull widget consists ListView() through its context...

I think what you are looking for is byType method.
This will find widgets by searching for widgets with a particular type.
So in your case, you'll have something like this:
expect(find.byType(ListView), findsOneWidget);

Related

how flutter's find() finds widget on widget testing?

I'm learning flutter's widget testing
and I'm reading flutter's official documentation of widget testing.
I wonder how find() method finds the widget.
find.byKey() may find by look around widget tree that which widget has specific key,
but like find.byWidget() how does it finds specific widget?
The find.byWidget() method finds a widget by checking if it is equal to the widget passed as an argument to the method. This is typically done by checking if the runtimeType and key of the two widgets match.
For example, let's say you have a Text widget with a specific key and you want to find it using the find.byWidget() method. You would first create the Text widget and assign it a key, like so:
final myText = Text(
'Hello World',
key: Key('my_text'),
);
Then, you can use the find.byWidget() method to find this widget in the widget tree, like this:
final foundWidget = find.byWidget(myText);
This will return the Text widget if it is found in the widget tree, or null if it is not found. You can then use this widget reference to perform assertions or interact with the widget in your tests.
Keep in mind that the find.byWidget() method only checks for strict equality between the two widgets. This means that if you have multiple widgets with the same runtimeType and key, the find.byWidget() method will only return the first widget it encounters that matches the criteria. To find all widgets that match the criteria, you can use the find.descendant() method instead.
going inside Flutter's source code, we will find that the find.byWidget() is implemented like this:
Finder byWidget(Widget widget, { bool skipOffstage = true }) => _WidgetFinder(widget, skipOffstage: skipOffstage);
going more inside _WidgetFinder:
class _WidgetFinder extends MatchFinder {
_WidgetFinder(this.widget, { super.skipOffstage });
final Widget widget;
#override
String get description => 'the given widget ($widget)';
#override
bool matches(Element candidate) {
return candidate.widget == widget;
}
}
so for flutter to find a specific widget, it compares it with the == operator with the one you provided.

How to make individual GetxControllers for reusable widget

I am trying to replace the use of Stateful widget with Getx. Most cases, I do not need it, however, when I am trying to create a list of cards widget, I find it hard to not to use the Stateful widget.
Obx(() {
getxA.updateA(name);
return Text(getxA.a());
})
RxString a = 'aaa'.obs;
updateA(String name) {
a(name);
}
When I have this Obx() - Text Widget inside the List Widget, it causes the error because each Text widget triggers updateA(String name) function.
It looks like every card is sharing one GetxController (getxA). Is there any way that to have each Text widget have its own GetxController? Or is it not possible in this case and I have to use the Stateful widget?

When using GetX observables, which UI widgets need to be wrapped in Obx?

To learn GetX I created a simple controller class:
class MyDataController extends GetxController {
RxString aString = ''.obs;
void updateString(String s) {
aString.value = s;
}
}
aString's value is displayed in two classes: the AppBar (not discussed here) and another class in which aString is both set and displayed:
class Level1 extends StatelessWidget {
#override
Widget build(BuildContext context) {
final MyDataController controller = Get.find();
final textController = TextEditingController();
return Column(
children: [
TextField(
controller: textController,
onChanged: (_) {
controller.updateString(textController.text);
},
),
Text(controller.aString.value),
],
);
}
}
I'm confused about which widgets need to be wrapped in an instance of Obx().
If I wrap only the Text() (display widget) in an Obx instance, it's updated when the TextField() (input widget) changes. And if I wrap only the TextField() widget in an Obx instance, I get an error message:
The following message was thrown building Obx(has builder, dirty,
state: _ObxState#019a0):
[Get] the improper use of a GetX has been detected.
You should only use GetX or Obx for the specific widget that will be updated.
If you are seeing this error, you probably did not insert any observable variables into GetX/Obx
or insert them outside the scope that GetX considers suitable for an update
(example: GetX => HeavyWidget => variableObservable).
If you need to update a parent widget and a child widget, wrap each one in an Obx/GetX.
Everything seems clear: widgets displaying state must be wrapped in Obx() instances to display updated variables. That makes perfect sense. And widgets that change state don't need to be wrapped in Obx() instances.
I'm confused, though, because if I wrap both widgets in separate Obx() instances, I get the error message. But if I wrap the entire Column() in an Obx() instance, the text is properly updated when the TextField() changes. ... What am I missing in my understanding?
You got it all up to the point when you wrapped the Text widget in an Obx. In actual fact it is best to wrap only the smallest widget that would need updating in Obx, the Text widget in this case.
Let me explain what happened the cases in which you tried testing it out:
Case 1: When you wrapped only the Text widget in Obx (The best thing to do)
This case happen to be the best and most encouraged approach. In this scenario when the value of aString changes in the controller (MyDataController) the Obx is notified to re-build only the affected Text widget from scratch, and this is exactly the aim of GetX.
Case 2: When you wrapped both the Text && the TextField widget in Obx (This will throw an error).
In this case you have wrapped both the Text and TextField widget in Obx, we can therefore let case 1 account for the Text widget.
Now, moving unto the TextField widget, an error will occur because the TexField widget is not in any way dependent on any obs-value (observable value).
It is important to note that in the onChanged callback provided to the textField, the method updateString called on the controller have no effect whatsoever on TextField's parameter and thus this leads GetX to throw an error since you are trying to forcibly update/re-build a widget that needs no rebuilding.
Case 3: When you wrapped the whole column in Obx (Will not throw an error but not the best practice).
In this case the widget will be built with no error whatsoever since the Text widget (which is inside the Column) is dependent on the value of aString. So, let's see what happens when the method updateString is called.
When the updateString is called the whole Column is re-built (along with the TextField and the Text widgets and this action will as well cause the value in the Text widget to be updated.
Now, you can see why this third case can be detrimental, if you try wrapping your whole app in Obx, your whole app will then have to get re-built (which can really affect your app's performance negatively. Of course GetX has a way of disallowing that and thus it throws an error when you try wrapping an HeavyWidget in Obx or GetX.

Flutter: How to check if Widget is a scrolling widget

Widgets like ListView, GridView, SingleChildScrollView etc are scrollable widgets while Container, SizedBox, Column are not.
Is there any way to check if the Widget is scrollable, using something like
Widget widget = SomeWidget();
bool scrollable = widget.isScrollable(): // any property like this?
When you write something like this:
Widget widget = SomeWidget();
then you are basically Downcasting your widget to Widget class, which is the parent of all classes. If you check the Widget class in doc you should see only 3 methods exposed. Which are:
createElement
debugFillProperties
toStringShort
bool scrollable = widget.isScrollable(): // any property like this?
So no, this is not possible.
However, you know that the widget has to be a subclass of ScrollView or is of type SingleChildScrollView so you can write a utility method for yourself. Like
bool isScrollable(Widget widget) => widget is ScrollView || widget is SingleChildScrollView;

onTap replace widget from another file flutter

Say my App() contains some widget and to make the code look pretty, i have created classes for child widgets. Now these child widgets contain onTap functions which are supposed to replace a widget on App(), so how do i approach this type of problem ?
Some code of what you're doing would be useful or where you're trying to "replace a widget".
If by replace you mean show a different widget in the place of another then you just use a boolean to decide which one to show. These are the steps I would follow to implement this.
Make the widget in your App() stateful and create a member variable boolean called showingOriginalWidget = true;
In your child widget classes take in a Function in the parameter called onSwapWidget.
In your onTap function in your child widget call onSwapWidget()
In your App() supply the widget that's performing this action with your Function to call back to
See below
childWidget(onSwapWidget: (){
setState((){
// toggle the original widget state
showingOriginalWidget = !showOriginalWidget;
});
});
Where you're showing your widgets add a condition so that you show either one depending on the value.
example
...
child: showingOriginalWidget ? originalWidget() : swappedOutWidget()
...
That should do the trick.