Is there a way to specify function parameters and return value as const in dart? - flutter

I wrote an extension function to add SizedBox between every child in Column or Row to add space between child, instead of putting SizedBox between every child, for which I didn't find any other way around.
Column(
children: [
// widgets
].setSpace(height: 10),
)
Row(
children: [
// widgets
].setSpace(width: 10),
)
So here List<Widget> setSpace({double? height, double? width}) takes height or width for the SizedBox used between child. But since height and width are not const I cannot use const SizedBox. So is there any way in dart to say that both the parameters and the return type will ALWAYS be cosnt? like const List<Widget> setSpace({const double? height, const double? width}) like C/C++?

I don't think that's possible, mostly because const can be applied only on constructors and fields, not on generic functions.
Maybe you can achieve that by creating your own widget that adds the SizedBox in its build method, and create a const constructor.
EDIT: here's a piece of code of mine of a custom widget with a const constructor.
class UnlockPage extends StatefulWidget {
final String pcData;
const UnlockPage({Key? key, required this.pcData}) : super(key: key);
#override
Widget build(BuildContext context) {
[...]
}
}
EDIT 2: here's a piece of code tested in DartPad. I don't think it can get better than this.
class SpacedColumn extends StatelessWidget {
final double height;
final List<Widget> children;
const SpacedColumn({required this.height, required this.children});
#override
Widget build(BuildContext context) {
var actualChildren = <Widget>[];
for (var child in children) {
actualChildren.add(child);
actualChildren.add(SizedBox(height: height));
}
return Column(
children: actualChildren,
);
}
}

You can't. As you pass a value this one can be different from one call to others.
Notice that const as not the same signification on Flutter than on other languages.
With Flutter it indicates to the rendering engine that the widget or the method is always the same and that the rendering engine is not obliged to rebuild this Widget when rebuilding the screen.
The keyword that act as const in other languages is final

In Dart language const doesn't mean the same as in other languages. You should use final if you don't want to change the values later.

Related

How to make a list of stateful widgets without passing inputs inside of the list?

I want to be able to randomly select certain widgets and use them as cards and then input values into their parameters. For example, if Ralph wanted three different fish and knew that he wanted to name them Jack, Piggy, and Simon, but his parents were buying and choosing the types of fish for him, how could we make a list of different fish at the store without names?
class Fish extends StatefulWidget {
const Fish ({
super.key,
this.color,
this.child,
this.name,
});
final Color color;
final Widget? child;
final String name;
#override
State<Fish> createState() => _FishState();
}
class _FishState extends State<Fish> {
String name = widget.name;
double _size = 1.0;
void grow() {
setState(() { _size += 0.1; });
}
#override
Widget build(BuildContext context) {
return Container(
color: widget.color,
transform: Matrix4.diagonal3Values(_size, _size, 1.0),
child: widget.child,
);
}
}
If I try to make a list of fish without naming them, it won't work since it needs me to input a name parameter. How can I avoid this or change the names afterward?
I would love to do something like this:
List<Widget> fishAtTheStore = [
Fish(color: Colors.red, child: Text("This is a fish")),
Fish(color: Colors.blue, child: Text("This is a fish")),
Fish(color: Colors.yellow, child: Text("This is a fish")),
Fish(color: Colors.green, child: Text("This is a fish")),
Fish(color: Colors.orange, child: Text("This is a fish")),
]
class RalphsAquarium extends StatefulWidget {
const RalphsAquarium({super.key});
#override
State<RalphsAquarium> createState() => _RalphsAquariumState();
}
class _RalphsAquariumState extends State<RalphsAquarium> {
#override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
fishAtTheStore[0](name: "Jack"),
fishAtTheStore[3](name: "Piggy"),
fishAtTheStore[1](name: "Simon"),
],
);
}
}
The actual functionality outside of the aforementioned issues and the required parameters does not matter to me.
Looks like you need some State Management here.
You've got a lot of libraries available to achieve this : Provider (which I recommend), Riverpod, Bloc are the most common ones (please avoid GetX)
Once you pick your State Management library, the logic to implement is the following :
Create a class Fish (not a widget, a model) which will hold all the params of your Fish.
class Fish {
Fish(this.name);
final String name;
}
Use this class in your Widget allowing to pick fishes
Create a "controller" which job will be to keep in memory the fish which will be picked
In this controller, you can add all your logic (like creating methods allowing you to update the name of the fish)
I strongly advise you to read this article of the flutter documentation first, to fully understand how to implement what you need

What is the best way to reuse a widget in Flutter?

In my app, I need to reuse a same divider more than 20 times.
Which way should I follow for best memory performance?
Way 1:
class DividerX extends StatelessWidget {
const DividerX({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Divider(color: Colors.green, height: 22);
}
}
Way 2:
class DividerX {
const DividerX._();
static const Widget divider = Divider(color: Colors.green, height: 22);
}
The second one. From Dart's website:
Const means that the object's entire deep state can be determined entirely at compile time and that the object will be frozen and completely immutable. (...) [Const objects] are canonicalized. This is sort of like string interning: for any given const value, a single const object will be created and re-used no matter how many times the const expression(s) are evaluated.

Pass parent state to generic child widget

I'm building a flutter app, and I have built a customized AppBar widget for it. This appbar has a SearchBar widget, which calls whatever callback is passed to it onChange. Now, there are multiple screens that use this SearchBar, and each of them will do something different with the user input. But I've noticed that on each of the screens that use the appbar, I'd have to use a state to control the SearchBar inputted text. So, I'm trying to not have to create the state for every screen, and have a Widget that wraps my screens, and controls the input in it's state, and passes it's state down to the child I provide to this apps. This would be similar to React's Higher Order Components, which wrap another component and can pass props to it.
This seems to me like a good design pattern, but I don't know how to implement it. Since the child widget that I would pass to this second order component that would wrap my screens, won't be getting any info from it, the child is simply passed as a widget (note: the code is also doing other stuff, working as a general wraper to replace similar repetitive code in all of my screens):
class CustomScaffold extends StatefulWidget {
final ScreenInfo appBarInfo;
final Builder child;
final Widget bottomBarApp;
final Widget appBar;
final EdgeInsetsGeometry padding;
const CustomScaffold({
#required this.child,
Key key,
this.bottomBarApp,
this.appBar,
this.padding,
this.appBarInfo,
}) : super(key: key);
#override
_CustomScaffoldState createState() => _CustomScaffoldState();
}
class _CustomScaffoldState extends State<CustomScaffold> {
String searchTerm = '';
#override
Widget build(BuildContext context) {
return Scaffold(
body: SingleChildScrollView(
child: Column(
children: [
widget.appBar ??
GradientAppBar(
title: widget.appBarInfo.label,
searchBar: SearchBar(
callback: (String input) {
setState(() {
searchTerm = input;
});
},
placeholder: widget.appBarInfo.searchPlaceholder,
),
),
Padding(
padding: widget.padding,
child: widget.child,
),
],
),
),
bottomNavigationBar: widget.bottomBarApp ?? CustomBottomBarNavigator(),
);
}
}
I'm thinking that I could passa builder instead of a widget as the child, but I'm not sure how this would work. Also, I'm still learning bloc in general, so I'm not sure if it would be a good idea to use bloc for this. I'm guessing bloc's purpose is a little different, and would complicate this specific pattern.
Does this idea make sense? What would be the best way to implement it?
Thanks in advance.

How to use SizeChangedLayoutNotifier?

I got a Wrap inside my flexibleSpace in my SliverAppBar. Now when the size of my Wrap changes (dynamically) I need to get a notification, so I can change the height of my flexibleSpace in my SliverAppBar accordingly.
I read the docs to the SizeChangedLayoutNotifier but I don't really understand how to use it. I wrapped my Wrap as a child in a SizeChangedLayoutNotifier but for me it is very unclear how to catch the notification with the NotificationListener<SizeChangedLayoutNotification>. I couldn't find any code example.
I would really appreciate your help :) Thanks!
I finally figured it out and will post the solution here for others, having the same problem.
I put my Wrap inside like this:
new NotificationListener<SizeChangedLayoutNotification>(
onNotification: gotNotification,
child: SizeChangedLayoutNotifier(
key: _filterBarChangeKey,
child: Wrap( // my WRAP stuff)
)
);
and having my callback like this:
bool gotNotification(SizeChangedLayoutNotification notification) {
// change height here
_filterBarChangeKey = GlobalKey();
}
I also found another solution from here not using SizeChangedLayoutNotification at all to solve my problem. For me this was even better. I just wrapped my Widget inside an MeaserSize widget which provides an onSizeChanged callback.
typedef void OnWidgetSizeChange(Size size);
class MeasureSize extends StatefulWidget {
final Widget child;
final OnWidgetSizeChange onChange;
const MeasureSize({
Key key,
#required this.onChange,
#required this.child,
}) : super(key: key);
#override
_MeasureSizeState createState() => _MeasureSizeState();
}
class _MeasureSizeState extends State<MeasureSize> {
var widgetKey = GlobalKey();
#override
Widget build(BuildContext context) {
WidgetsBinding.instance
.addPostFrameCallback((_) => widget.onChange(widgetKey.currentContext.size));
return Container(
key: widgetKey,
child: widget.child,
);
}
}

Standardizing sizes across application, but still use `const`

I'd like to standard size across a flutter application to comfort to a 4 pt grid. Here's one example of how this could be done:
class Spacing {
const Spacing(double val) : points = val * 4;
final double points;
}
class PtPadding extends Padding {
PtPadding({Spacing padding, Widget child}) : super(padding: padding.points, child: child);
}
PtPadding(padding: Spacing(4), child: Text('Hello'));
// or just with regular old Padding
Padding(padding: Spacing(4).points, child: Text('Hello'));
This is great, but it seems I forgo the ability to const my specialized PtPadding forces developers to use Spacing. On the other hand, just using Spacing in a constructor and accessing the points, prevents any widget from being "const"able. So it seems like I have to take a performance hit if I want to implement this spacing in my system.
I could have a class with static const members that point to doubles, but then I'm restrained to the sizes available (ie I can only have so many static members) and I also don't get the benefits of type restrictions.
I'm wondering if anyone else has thoughts in how I might approach this.
For what it's worth, I understand why Spacing(4).points is not a const (methods inherently aren't consts), but not sure how to get around this.
The problem is, you are extending Padding. Widgets are not made to be extended. Instead, you should use composition.
class Spacing {
const Spacing(double val) : points = val * 4;
final double points;
}
class PtPadding extends StatelessWidget {
const PtPadding({
Key key,
#required this.padding,
this.child,
}) : super(key: key);
final Spacing padding;
final Widget child;
#override
Widget build(BuildContext context) {
return Padding(
padding: EdgeInsets.all(padding.points),
child: child,
);
}
}