How to Implement Flutter Functions - flutter

With reference to a Stack Ordering solution (Flutter - changing a Stack Order)
Summary - I have a stack of widgets I want to reorder. The solution is to use functions to recreate the correct order of widgets. In the example linked to above works (great!). However, the example widgets were small - no issue to simply repeat the same code to re-order.
My real world example - the stack of widgets is reasonably complex and I have refactored into functions to reuse between the stack order functions:
Let's say the original stack has this as one of the widgets in the stack:
final statusMessage = new Container(
padding: new EdgeInsets.all(3.0),
margin: new EdgeInsets.only(bottom: 0.0),
child: new Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
new Text(
statusText,
style: themeData.textTheme.subhead,
),
],
),
alignment: Alignment.topCenter);
I have been declaring all these widgets in the single Widget build class.
The functions to reorder the stacks are outside of this build function. An example:
void swapStackChildren() {
setState(() {
stackChildren = [
Positioned.fill(
child: statusMessage,
key: GlobalKey(),
),
Positioned.fill(
child: widget2, // a complex widget
key: GlobalKey(),
),
etc....
];
});
}
Ideally, I need a way to (forward) reference the widgets in swapStackChildren that are declared in the build method or a way to reference 'context' outside of the build method so I can reference themeData.
Perhaps it is my lack of understanding of the structure/layout of a page? I have only one Widget build class where I am declaring all my widgets (header, content area, buttons, etc) and these cannot be referenced by functions in the preceding code.

Related

Flutter baseline text alignment with Wrap class

Using the Row class, I can make the text baseline aligned with this property:
crossAxisAlignment: CrossAxisAlignment.baseline,
But this property seems not to be available on the Wrap class.
The benefit of using Wrap instead of Row is that it allows having multiline text. While the Row class force the content to stay into one line.
How can I keep the benefit of Wrap, while making the text baseline aligned?
I also had this problem. There is an open issue in the Flutter Repo addressing this. While the built-in solution is not implemented yet, I did come up with a slightly-hacky workaround.
Basically, depending on your font and your needs...
Set your Wrap() to have crossAxisAlignment: WrapCrossAlignment.end,. Then you can make one of the children of your Wrap() a Column() that has a SizedBox() as the lowest child, with the height that you need to make it appear like your texts are using the does-not-exist WrapCrossAlignment.baseLine
Example of the problem:
Example of the solution as outlined above (I did point out that it's kind of hacky):
The Code:
class WrapBaselineHackWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return DecoratedBox(
decoration: BoxDecoration(border: Border.all(color: Colors.red)),
child: Wrap(
crossAxisAlignment: WrapCrossAlignment.end,
spacing: 8,
children: [
Text(
'\$50,000.00',
style: TextStyle(fontSize: 24),
),
Column(
children: [
Text('Is a lot of money'),
//this is needed because Wrap does not have WrapCrossAlignment.baseLine
const SizedBox(height: 3),
],
),
],
),
);
}
}
Keep using Row and simply wrap Text widget with Expanded or Flexible widget.

Flutter two widgets in body?

So my flutter app works fine, however I want to make some changes to it.
The 1st change I want to do is create a horizontal scroll widget that has images that are clickable that change stations.
But to do this I first need to join two widgets into one.
In my home_widget.dart file I have this code:
final List<Widget> _children = [TracksWidget(),
NewsWidget(),
AboutWidget()];
The code above changes the body section of the home_widget.dart section. But now what I want to do is add StationsWidget() to the bottom of the TrackWidget() - Note this widget refreshes every 30 seconds. So ideally I need to make sure it does not refresh with it.
So can a body have two widgets?
Full code of test.dart (which is where I am trying to add the widget)
import 'package:flutter/material.dart';
import 'trackswidget.dart';
class TestWidget extends StatelessWidget {
// final Color color;
// PlaceholderWidget(this.color);
#override
Widget build(BuildContext context) {
double c_width = MediaQuery.of(context).size.width;
return new Container (
padding: const EdgeInsets.all(16.0),
margin: const EdgeInsets.only(bottom: 0.0),
width: c_width,
child: new SingleChildScrollView(
child: new Column (
children: <Widget>[
Column(
children: <Widget>[
Child: TracksWidget()
],
),
Column(
children: <Widget>[
new Text('Advertise on ',style: TextStyle(color: Colors.black, fontSize: 20.0),),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children:
),
],
),
]
),
),
);
}
}
The error I am getting is
'Widget' is from 'package:flutter/src/widgets/framework.dart' ('/C:/src/flutter/packages/flutter/lib/src/widgets/framework.dart').
Try changing the type of the parameter, or casting the argument to 'List'.
children: TracksWidget,
Just so you all know what I am trying to achieve. We redesigned our iOS Swift app and now want our flutter app to match.
Strictly speaking, no, the body takes exactly one Widget. You could say it takes none if you pass null, but you cannot pass more than one.
However, there are widgets that group other widgets. Your one body widget could as well be a Row and that row can have multiple child widgets.
You already did that in your title.
A nice graphical representation can be found in the documentation.

Display a built list of widgets in Dart

I've got a list of widgets I've built with a for in loop within a function but I'm unsure as how to display all the widgets as children of a column.
The function displayAllCards() builds the list, each widget starts with padding, i don't know if that helps or not.
Here's a very stripped down version of my unworking code.
The function to build a widget:
Widget displayCard(String bitCurrency) {
return Padding(
padding: EdgeInsets.fromLTRB(18.0, 18.0, 18.0, 0),
child: Text("$bitCurrency"),
);
}
And then I built another function to create a list of widgets throwing in different bitCurrencies:
List displayAllCards() {
List<Widget> cards = [];
for (String bitCurrency in cryptoList) {
cards.add(displayCard(bitCurrency));
}
return cards;
}
And finally the output with the Flutter is where I'm dying.
body: Column(
children: <Widget>[
displayAllCards(),
],
),
I kinda know why it's not working, but I'm unsure how to make it correct.
I'm fairly new to Dart so please be gentle.
Just use spread operator (...)
body: Column(
children: <Widget>[
...displayAllCards(),
],
),

Simple Layout Challenge

I am trying to create a Card that displays the score in a game. The card will be more complex than this example, but in the middle of the card I want to have a Row. That Row contains two Columns. The first Column contains two Rows. In each of those two Rows is the team name and the score.
Widget build(BuildContext context) {
return Card(
child: Row(
children: <Widget>[
Column(
children: <Widget>[
Row(
children: <Widget>[
Text('Team A'),
Text('100'),
],
),
Row(
children: <Widget>[
Text('Team B'),
Text('100'),
],
),
],
),
Text('15:00'),
],
),
);
}
Now I want t wrap the Text widgets that display the team name in a Expanded. Once I do that I get a long descriptive error that basically says my Row has a parent that does not have a finite width, but it has a child that is asking to expand and these two are in conflict. I get that they are in conflict. How do I fix it? I definitely don't want to give the parent Widget (the Card) a finite width.
It's not 100% clear what you want the scoreboard to look like - this is an instance where a quick mock-up would be a great help.
However, I can tell you why you're getting an exception.
When you wrap your 'Team A' text in an Expanded, during the layout pass flutter gets confused. The top level row has two items, neither of which define any sort of size, so it makes them as large as possible in the cross axis (vertical) and as small as possible in the main axis (horizontal). This means that they delegate their size to their children.
By wrapping 'Team A' in an Expanded, you're basically telling it:
"Hey you, your parent widget is pretty big. I want you to take up all of it that isn't already being used instead of being how big you would normally be."
It responds with
"Sure boss, but I've just checked and none of my parents actually have
a defined size.... so I don't know what to do. Guess I'll just throw an
exception."
The fix is to size the parent, which in this case is the column that contains the two rows (geez, that's confusing).
I think from your description what you want is for that column to take up as much space as possible, so you just have to wrap it in expanded as well. So if you do this, your problem should resolve itself:
Widget build(BuildContext context) {
return Card(
child: Row(
children: <Widget>[
Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Row(
children: <Widget>[
Expanded(child: Text('Team A')),
Text('100'),
],
),
Row(
children: <Widget>[
Expanded(child: Text('Team B')),
Text('100'),
],
),
],
),
),
Text('15:00'),
],
),
);
}
That way, the column containing the two rows expands to take all available space during its layout pass. Once the 'Team A' text gets around to trying its layout pass it can simply size itself (horizontally) to that column.
You can then play around with the flex factor to get everything how you want.
Just an FYI - once you start getting lots of nested containers and rows you might want to consider a CustomMultiChildLayout. It's slightly more manual as you need to do the layout calculations yourself, but it might be easier in the long run depending on how complex your scoreboard is.

Positioned widgets must be placed directly inside Stack widgets

I am trying to implement gridview with image and a text inside it. where i want text at the bottom of image with black background. Here is my code for ListItem
class ListItem extends StatelessWidget {
String url;
String name;
ListItem(this.url, this.name);
#override
Widget build(BuildContext context) {
return new Container(
child: new Column(
children: <Widget>[
new Image.network('${url}', fit: BoxFit.cover),
new Positioned(
child: new Container(
child: new Text('${name}',
style: new TextStyle(fontSize: 20.0, color: Colors.white)),
decoration: new BoxDecoration(color: Colors.black),
padding: new EdgeInsets.fromLTRB(16.0, 16.0, 16.0, 16.0)),
left: 0.0,
bottom: 108.0,
)
],
));
}
}
With this code it is showing error
Positioned widgets must be placed directly inside Stack widgets.
Positioned(no depth, dirty) has a Stack ancestor, but there are other widgets between them:
- Column(direction: vertical, mainAxisAlignment: start, crossAxisAlignment: center)
Issue was with Column, after changing few lines from here and there i finally found that it was because of Column
Once i change Column to Stack, it works fine.
return new Container(
child: new Stack(
We were just discussing this yesterday. Positioned can actually be used in more than just a Stack, so the docs aren't exactly right about that. It can't be used in anything that renders, and the docs are very specific about RenderObjectWidget:
"A Positioned widget must be a descendant of a Stack, and the path from the Positioned widget to its enclosing Stack must contain only StatelessWidgets or StatefulWidgets (not other kinds of widgets, like RenderObjectWidgets).
Source: https://docs.flutter.io/flutter/widgets/Positioned-class.html
Column is descended from RenderObjectWidget:
... Widget > RenderObjectWidget > MultiChildRenderObjectWidget > Flex > Column
Most people starting out in Flutter are only aware of StatelessWidget and StatefulWidget, but there are others and knowing them can be very important at times.
Widget:
StatefulWidget
StatelessWidget
RenderObjectWidget
ProxyWidget
PreferredSizeWidget
More at:
https://docs.flutter.io/flutter/widgets/Widget-class.html