I'm looking into how to write my own widgets. I've seen a number of examples that have a CustomWidget that either inherits from StatefulWidget or StatelessWidget. Is it possible to inherit from Stack or TextBox or one of the widgets that I've seen returned from build? What I'm thinking of is taking a widget like TextBox, adding custom theming, and then exporting it as a new widget to use.
I've seen a lot of examples using Material UI. For my needs, I need something less cookie-cutter that let's me deside what things are meant to look like how they should style.
Thanks.
Yes, you can create a widget extending some other widget but you will probably need to conform to its super constructor.
You are talking about TextBox in your question but it is not a widget so I'm guessing that you were talking about the Text widget or a similar one. Here's a code sample of how you could create your own widget :
class MyText extends Text {
const MyText() : super("my-text"); // Need to pass a data value to the super constructor.
/// You can override the base build method to return a different widget,
/// in this case I am returning the base super.build (which will render
/// the Text widget) wrapped inside a red Container.
#override
Widget build(BuildContext context) {
return Container(
color: Colors.red,
child: super.build(context),
);
}
}
Try the full test code on DartPad
What I'm thinking of is taking a widget like TextBox, adding custom theming, and then exporting it as a new widget to use.
Then, creating a new custom widget by extending TextBox in not the way to go.
If you do that, you will have to hardcode every properties of the constructor of TextBox in yours. It will be a pain to do and to maintain.
FLutter base principle in Composition over Inheritance.
So, in order to achieve what you need (Theming), you just have to use Theme capabilities :
// global theming once and for all for all the app
MaterialApp(
theme: ThemeData(inputDecorationTheme: InputDecorationTheme()),
home: const MyHomePage(),
);
or
// located theme overload just for here
Theme(data: Theme.of(context).copyWith(inputDecorationTheme: InputDecorationTheme()), child: TextFormField());
Theming only may be not enough but you really should avoid inheriting complex native widgets, prefer composition of property factorisation
Related
In the "Performance considerations" section of StatefulWidget documentation, third point mentions the following:
If a subtree does not change, cache the widget that represents that subtree and re-use it each time it can be used. To do this, assign a widget to a final state variable and re-use it in the build method. It is massively more efficient for a widget to be re-used than for a new (but identically-configured) widget to be created. Another caching strategy consists in extracting the mutable part of the widget into a StatefulWidget which accepts a child parameter.
Its not clear that what is "state variable" here, and what to to assign, because "build" term is mentioned in that line, and there is also a build method for every widget,
I mean,
Should the user do final Widget widget = MyWidget(); or final Widget widget = MyWidget().build(context);
As they both return Widget,
Also please explain, that is there any difference between the above statements, and what is the use of Builder widget, if we have build method, or do they have different work, if so, then what ???
thanking you
Never, ever ever ever run the build method on your own, the build method is there for flutter to call for itself with an updated context, you should not be using it. Always do MyWidget()
The Builder widget is useful when you need a newer context for something inside your widget, for example:
Widget build(BuildContext context) { // context crated here
return MaterialApp( // navigator created here, after the context was defined
home: ElevatedButton(
onPressed: () => Navigator.of(context).pop()
),
);
}
This code won't work because the context was created before the navigator (which was created along with a material app), you could wrap the button inside a builder widget to get a newer context that already has a navigator
Of course, calling the build method on your own is not good because you won't be passing the correct context to the widget, instead, let flutter handle the context creating and just use normal constructors.
What is the best way to make custom widgets in Flutter that don't conflict with those exported by the material package? Specifically: I'm trying to make a design system based on atomic design. So, let's say I want my own Text and Card widgets. I can't name them Text or Card because both of these are exported by the material package.
Some options that I've considered:
Creating a ui library and importing using as
// components/atoms/Text.dart
import "package:flutter/material.dart" hide Text;
import "package:flutter/material.dart" as material show Text;
class Text extends StatelessWidget {
Text(String content);
Widget build(BuildContext context) {
// Imagine some more complicated styling specific to my design system
return material.Text(content);
}
}
// components/ui.dart
library ui
export "package:my_app/components/atoms/Text";
// screens/HomeScreen.dart
import "package:flutter/material.dart";
import "package:my_app/components/ui" as ui;
class HomeScreen extends StatelessWidget {
Widget build(BuildContext context) {
// Example usage
return ui.Text(content);
}
}
This doesn't feel great... First, I need to remember to include as whenever I import the ui library, and second, it's slightly annoying to have to hide and show classes in my Text implementation.
Just hide the conflicting classes
// screens/HomeScreen.dart
import "package:flutter/material.dart" hide Text;
import "package:my_app/components/Text";
class HomeScreen extends StatelessWidget {
Widget build(BuildContext context) {
// Example usage
return Text(content);
}
}
Similar to option (1), I don't want to have to remember to hide all the classes that conflict with my custom widgets.
Just use different names
Maybe AppText or ThemeText or UIText or AtomText? This doesn't feel great either, especially for some of the other widgets like IconButton.
Is there a convention around this, or some decent solution? I haven't seen any custom UI libraries, but this is mostly useful for teams that want to reuse widgets across apps and keep a consistent style.
From what I gather, you want your custom Text class to do the exact same thing as a normal Text widget, but with a different design. If that really is the case, I propose that you change the ThemeData of your project. The MaterialApp widget takes a theme argument, so you can style your TextTheme, CardTheme, IconButtonTheme etc. in there. This would prevent any naming conflicts, save you a lot of coding, and makes it easy to tweak.
Things such as TextTheme allow for different types of styles, such as headlines, bodytext, etc., as described here.
You can add 'My' at the beginning of the widget, for example, 'MyText' instead of 'Text' and 'MyCard' instead of 'Card'.
You can use a tricky way, in my case I use an intentionally wrong dictation for naming. For instance, instead of 'Card' you can name 'Cart', or instead of 'Text', you can name tekst.
I was learning flutter and came across this code:
class MyStatelessWidget extends StatelessWidget {
final String name;
MyStatelessWidget(this.name);
#override
Widget build(BuildContext context) {
return Text('Hello, $name!');
}
}
Sorry, I would like to ask some questions on the above code. Firstly, why need to use #override, that is,I know it is needed for method overriding but is it true that build method in StatelessWidget is defined like this build(){} therefore we need to override it and add some logic? Secondly, here Widget build Does Widget mean that build returns a Widget? Thirdly, why do we need to use BuildContext here build(BuildContext context)?
CONTEXT
From the docs, BuildContext is:
A handle to the location of a widget in the widget tree.
context is a BuildContext instance which gets passed to the builder of a widget in order to let it know where it is inside the Widget Tree of your app.
One of the common uses is passing it to the of method when using an Inherited Widget.
Calling Something.of(context), for example, returns the Something relative to the closest widget in the tree that can provide you that Something.
BUILD METHOD
Build method is required because it describes the part of the user interface represented by this widget.The framework calls this method in a number of different situations.
Read more about build method here Build Method
STATELESS WIDGET
A widget that does not require mutable state.
A stateless widget is a widget that describes part of the user interface by building a constellation of other widgets that describe the user interface more concretely.
Read more about stateless widget here Stateless widget
I hope this helps.
I use a lot StatelessWidgets when I have to create "templates" of widgets that are used multiple times inside my app because the docs say so:
Stateless widget are useful when the part of the user interface you
are describing does not depend on anything other than the
configuration information in the object itself and the BuildContext in
which the widget is inflated.
Here is an example:
class StepInputButton extends StatelessWidget {
final int pos;
final String value;
const StepInputButton({
this.pos,
this.value
});
#override
Widget build(BuildContext context) {
return Row(
// Text, Icon and a tiny button
);
}
}
The above is good because I can use const StepInputButton(val, "val"), in the code with CONST which improves performances.
PROBLEM
I am using the famous Provider widget to manage the state and the page of my apps usually look like this:
class SuccessPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
var prov = Provider.of<Type>(context);
return Scaffold(...);
}
}
That's a page of my app with Scaffold that has a Drawer, a float action button and an appTitle.
Here I use a StatelessWidget because I do not use setState() since provider does all the work for me. But still in the official flutter doc they say:
For compositions that can change dynamically, e.g. due to having an
internal clock-driven state, or depending on some system state,
consider using StatefulWidget.
So do I have to change class SuccessPage extends StatelessWidget to class SuccessPage extends StatefulWidget? Do I get advantages?
Note: if you want to put the question in another way: should I use StatefulWidgets to create "app pages" whose state is going to change and StatelessWidgets for "reusable widgets" whose state doesn't change?
StatefulWidget is necessary for when the widget itself is maintaining its own state. In the example you gave, the Provider package is handling the state for you, assuming you're using the correct provider type higher up the widget tree (for example, ChangeNotifierProvider). There also doesn't seem to be anything in this code that would benefit from having access to the widget's lifecycle, so you wouldn't need access to methods like initState or dispose.
As such, there's nothing for the widget itself to manage, so converting your class to be stateful is unnecessary.
One thing I might suggest, though, is to use a Consumer instead of calling Provider.of directly. A Consumer handles the call for you and removes any ambiguity on whether your widget will get updated when the Provider detects a state change.
You use StatelessWidget for widgets that don't change their state, that will stay the same all the time. Example, appBar is stateless.. The build(...) function of the StatelessWidget is called only once and no amount of changes in any Variable(s), Value(s) or Event(s) can call it again.
Therefore, when you need to change state(ex value) then use StatefulWidgets, basically StatelessWidget is used for building UI widgets that are static
Keeping it simple:
If you have non-final global variables in your widget then you need a StatefulWidget
If all global variables are final then you should use StatelessWidget;
Reason:
If your global variable is non final that means it is allowed to change and if it's value is changed that means state of your object(Widget) is changed (basic oops concept I am talking about). In such case you would like to call build method of your widget so that your changes get applied on the UI (if it matters for your UI). We do it by calling setState(); and so we use StatefulWidget for such use-case.
If it is enough that once you initialize your global variable in constructor, you don't need to assign it any value in future then in such case use StatelessWidget.
I have tried to keep it very simple and not technical enough so, if you still have any doubts please comment on this answer.
I am using Provider. I have got two classes: class TenderApiData {} it's stand alone class (not widget). How I can write accesstoken to AppState?
class AppState extends ChangeNotifier // putted to ChangeNotifierProvider
{
String _accesstoken; // need to fill not from widget but from stand alone class
String _customer; // Fill from widget
List<String> _regions; // Fill from widget
List<String> _industry; // Fill from widget
...
}
I need way to read\write accesstoken from stand alone classes.
Or I have issue with architecture of my app?
Here is full source code.
You cannot and should not access providers outside of the widget tree.
Even if you could theoretically use globals/singletons or an alternative like get_it, don't do that.
You will instead want to use a widget to do the bridge between your provider, and your model.
This is usually achieved through the didChangeDependencies life-cycle, like so:
class MyState extends State<T> {
MyModel model = MyModel();
#override
void didChangeDependencies() {
super.didChangeDependencies();
model.valueThatComesFromAProvider = Provider.of<MyDependency>(context);
}
}
provider comes with a widget built-in widgets that help with common scenarios, that are:
ProxyProvider
ChangeNotifierProxyProvider
A typical example would be:
ChangeNotifierProxyProvider<TenderApiData, AppState>(
initialBuilder: () => AppState(),
builder: (_, tender, model) => model
..accessToken = tender.accessToken,
child: ...,
);
TL;DR
Swap provider for get_it. The later does DI globally without scoping it to a BuildContext. (It actually has its own optional scoping mechanism using string namedInstance's.)
The rest...
I ran into a similar problem and I believe it comes down to the fact that Provider enforces a certain type of (meta?) architecture, namely one where Widgets are at the top of what you might call the "agency pyramid".
In other words, in this style, widgets are knowledgable about Business Logic (hence the name BLoC architecture), they run the show, not unlike the ViewController paradigm popularised by iOS and also maybe MVVM setups.
In this architectural style, when a widget creates a child widget, it also creates the model for the widget. Here context could be important, for example, if you had multiple instances of the same child widget being displayed simultaneously, each would need its own instance of the underlying model. Within the widget or its descendents, your DI system would need the Context to select the proper one. See BuildContext::findAncestorWidgetOfExactType to get an idea why/how.
This architectural style is the one seemingly encouraged by plain vanilla Flutter, with its paradigms of app-as-a-widget ("turtles all the way down"), non-visual widgets, layout-as-widgets and InheritedWidget for DI (which provider uses I believe)
BUT
Modern app frameworks libs (e.g. redux, mobx) encourage the opposite kind of meta-architecture: widgets at the bottom of the pyramid.
Here widgets are "dumb", just UI signal generators and receivers. The business logic is encapsulated in a "Store" or via "Actions" which interact with a store. The widgets just react to the relevant fields on the store being updated and send Action signals when the user interacts with them.
Which should you use?
In my experience, at least on mobile where the screen realestate is less, scoping a model to a branch in the render tree is seldom required. If it suddenly becomes important then there are plenty of other ways to handle it (indexed array, id lookup map, namedInstances in get_it) than to require linking it to the semantics of UI rendering.
Currently, having spent too much time in iOS ViewControllers, I'm a fan of new systems which enforce better SoC. And personally find Flutter's everything-is-a-widget pardigm to appear a bit messy at times if left untended. But ultimately it's a personal preference.
you can use navigator key
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
and put this key in MaterialApp and wrap it with your provider (TenderApiData)
ChangeNotifierProvider<TenderApiData>(
create: (_) => TenderApiData(),
child: Consumer<TenderApiData>(builder: (context, tenderApiData , child) {
return MaterialApp(
navigatorKey: navigatorKey,
title: 'title',
home: SplashScreen());
}),
);
and listen to this provider from anywhere with this navigator key
navigatorKey.currentContext?.read<TenderApiData>();