Flutter: How do I stretch the last Element in a Wrap Widget? - flutter

I think this doesn't need much explaining so I didn't include any code or screenshots.
As the title, suggests, I have a Wrap Widget and I want the last Element of it to take up all the available horizontal space to the right, (the same way as if you put an Expanded Widget in a Row).
I this case though, I can not use Expanded or Flexible, as Wrap doesn't allow that.
Edit, added a bit of (simplified) code and screenshots:
_buildTileDragTarget is just a function that returns a DragTarget.
void _buildAnswerWidgetsFromStrings() {
_answerWidgets.clear(); //start building from 0
_answerWidgets.add(_buildTileDragTarget(index: 0)); //add initial DragTarget
//for each word in _answerStrings, add Draggable Chip & DragTarget
for (int i = 0; i < _answerStrings.length; i++) {
WordTile wordTile = _answerStrings[i];
_answerWidgets.add(
Draggable<WordTile>(
key: UniqueKey(),
data: wordTile,
child: GestureDetector(
behavior: HitTestBehavior.opaque,
child: Chip(
label: Text(wordTile.word),
padding: EdgeInsets.all(0),
),
onTap: () {
//seems to work: tap from top to bottom (5)
setState(() {
_options[wordTile.word] = true;
_answerStrings.remove(wordTile);
});
},
),
feedback: Material(child: Chip(label: Text(wordTile.word))),
childWhenDragging: Chip(label: Text(' ' * wordTile.word.length)),
),
);
_answerWidgets.add(_buildTileDragTarget(index: i + 1)); //the position AFTER the current word (last)
}
//remove last DragTarget and add one wrapped in Expanded
_answerWidgets.removeLast();
_answerWidgets.add(
SizedBox(
// width: double.infinity,
child: _buildTileDragTarget(index: _answerStrings.length),
),
); //TODO try wrapping the whole thing in 1 big DragTarget (for last index)
// _answerWidgets.add(_buildTileDragTarget(index: _answerStrings.length));
}
Screenshots:
I wrapped the Wrap widget in a purple Container, each DragTarget is wrapped in a green Container. (you can ignore the blue part)
(1) This is what I have (without SizedBox)
(2) This is what I get when I try using SizedBox(width:double.infinity...)
(3) This is what I want

Try SizedBox(width: double.infinity)

There's also expand constructor you might use
SizedBox.Expand(
child: //your widget
)

Related

Flutter List.generate but in reverse?

I have a container that I am trying to print out items form a list, minus the last item in the list. I am using list.generate:
child: Container(
width: double.infinity,
color: Colors.white,
child: ListView(
padding: const EdgeInsets.only(top: 10.0),
children: List.generate(
Provider.of<WeekList>(context).listOfWeeks.toList().length -
1,
(index) => ListTileCustom(
index: index,
),
),
),
),
My problem is when it prints out my custom ListTileCustom widget each new item added to the list is added to the bottom and not the top in the view.
In the picture above the order should be:
Red Bar
Red / Green Bar
Green bar
and when I add new items by pressing the button they should be added to the top not the bottom.
I tried adding reverse: true and that gets the order right but moves everything to the bottom and adds a ton of white space above them... Also not sure the scroll will work in the right direction at that point either.
To reverse a list in Dart, you can use ..reversed method. In the code you posted, that would be: children: List.generate(/* omitted */).reversed.toList().
Another (probably better) solution is to use for loop directly in children, for example:
ListView(
children: [
for (int i = 0; i < 5; i++) FlutterLogo(),
],
)
With this approach you can easily invert the list (and omit one element, like you desired) by changing the loop, e.g. for (int i = 10; i > 1; i--) or something.

Moving a dynamicaly generated Positioned Widget in Flutter

How can I move an Positioned Widget in Flutter that was created at runtime?
Szenario:
Lets say I have a Stack Widget. The children are a variable of type List<Widget>[].
I receive the command to create a new Positioned Widget.
A second stream sends updated x and y positions, those I want to apply on left and top of the dynamically created Positioned Widget.
To do this I added two variables to my class and set them to left and top of the Positioned Widget.
While this works as along as the Positioned Widget is created during build time, it does not work when I dynamically add the Widget to the List<Widget> at runtime. (the Positioned Widget appears, but i can not change left/top through the variables of _MyClass)
Some pseudo code that shows the bits.
class _MyClass extends State<StatefulWidget> {
double x = 0.0;
double y = 0.0;
List<Widget> myList = [];
[...]
void _updateLocation(dx, dy) { // called by a stream...
setState(() {
x = dx;
y = dy;
});
}
[...]
void addHeart() {
myList.clear();
myList.add (
Positioned(
top: y,
left: x,
child:Icon(
Icons.favorite,
color: Colors.pink,
size: 24.0,
),
)
);
}
[...]
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text("Demo"),
),
body: Center(
child: Container (
child: Stack(
children: myList, //this does not update the top/left of the object
/* //THIS WORKS
children: <Widget>[
Positioned(
top: y,
left: x,
child:Icon(
Icons.favorite,
color: Colors.pink,
size: 24.0,
),
)
]
*/
)
)
),
);
}
I'm new to Flutter, and I propably don't get the mechanics right here, though, I tried to get this working with setState and Keys, but I feel like I'm missing a important bit of information here.
A possible hack I found is to provide the List<Widget> through a function to the Stack Widget and rebuilding the List<Widget> everytime I get an update on x and y. Which essentially deletes and adds the Widget from the stack everytime I get a new x and y... This seems like the worst way to achieve what I want.
How can I do this in a better way, what I'm missing here, why can I update top/left through x and y when creating the widget in build(..) but not when adding it to the list at runtime?
Ofcourse it would not work when you have a predefined list as children of stack. For your widget to move and change its location, flutter needs to actually rebuild the changed widgets, and note that it does call build method each time it re-renders.
So it is not a bad solution to call a function which creates the list each time (as it is exactly what flutter does for all widgets).
You do not need to worry about adding and deleting the items of stack each time the function is called, that is because flutter does not actually re-render anything on the page if it has not changed compared to current content of the page.

flutter scollable.ensurevisible not working in reverse direction

Please check the video / gif:
I have a pageview that will make the current tab active. I need to ensure the active tab is always visible even if the user swipes the screen multiple times. It is working from the left to right. But when we try back from right to left it's not behaving as expected.
PageView file
PageView(
controller: pageController,
onPageChanged: (int page) {
_duaWidgetState.currentState.updateBtn(page + 1);
Scrollable.ensureVisible(
_duaWidgetState.currentState.activeBtn.currentContext);
},
children: loaded
TabBarWidget with scroll view file
GlobalKey activeBtn = GlobalKey();
var _selectedTab = 1;
#override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.symmetric(vertical: 20),
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
children: List.generate(widget.numberOfTab,
(index) => tabBarItem(index + 1, widget.numberOfTab, activeBtn)),
),
),
);
}
Container that uses the Key
Container(
key: activeBtn,
margin: EdgeInsets.only(left: 20),
padding: EdgeInsets.symmetric(
vertical: 10,
horizontal: 24,
),
No One answered my Question. But, after a lot of effort and time. I found a workaround to fix this problem. Hope this is a bug and will fix by the flutter team in the coming updates.
Scrollable.ensureVisible(_duaWidgetState.currentState.activeBtn.currentContext);
This function has an optional argument called alignment which can be adjusted to make sure the selected element/button is in the center of the viewport. (In my case horizontally center).
My widgets are horizontally scrollable, so I need to update the page number according to the result from PageView(), Here when I used
alignment: 0
Its working fine from left to right swipes of page view. But with this alignment when i swipe page from right to left to go to previous page in the PageView widget, the ensureVisible is not working like expected. The selected element/button is out of the view. I experimented and found that when I used
aligment: 1
The ensureVisible is working fine from the swipes from right to left but at the same time. when I scroll left to right the same problem occured.
Finally, so I managed to do some workaround to fix this behavior. The solution is to store last page index in a variable and check whether the last page is greater than the new page then alignment: 0 and if the last page less than the new page aligment:1 like that.
PageView(
onPageChanged: (int page) {
if (lastPage < page) {
Scrollable.ensureVisible(
_duaWidgetState.currentState.activeBtn.currentContext,
alignment: -0.0100,
);
} else {
Scrollable.ensureVisible(
_duaWidgetState.currentState.activeBtn.currentContext,
(Perfect For font size 24)
alignment: 1.005,
);
}}
Hope my answer will help someone in the future.
I modified the original ensureVisible() a bit and this works in both directions in my case:
// `contentAnchor` is the global key of the widget you want to scroll to
final context = contentAnchor.currentContext!;
final ScrollableState scrollable = Scrollable.of(context)!;
scrollable.position.ensureVisible(
context.findRenderObject()!,
duration: const Duration(milliseconds: 300),
curve: Curves.easeInOut,
);

Update Text() based on user input in a TextFormField() within a container in a ListVIew

I'm new to flutter and have a ... challenge with the above.
I retrive data about several objects / documents from FireStore and create a ListView.
The logic, or lack there of, look like this:
Container
StreamBuilder
ListView
Container
Rows, Columns, Text and whatever to display each document from StreamBuilder.
This ListView contains several Containers where each Container display data from one object. Within each Container there is a TextFormField that takes a double. Based on this value, I want to change a Text() within the same container - a calculation.
Is this possible? If so - how? I understand I can use TextEditController somehow, but then the result of tha calculation will be editable. Or?
return new ListView(//
children: getMedicationItem(asyncSnapshot
shrinkWrap: true,
children: asyncSnapshot.data.documents.map((DocumentSnapshot document) {
MyObject object = new MyObject(document, integerValue);
backgroundColor = !backgroundColor;
return new Container(
color: backgroundColor? Colors.black26: Colors.white,
child: new Row(
children: <Widget>[
new Flexible(
child: TextFormField(
onChanged: (text) {
double calculation = 0.0;
if (text.length > 0)
calculation = double.tryParse(text) * 23;
**UPDATE "$calculation" in Text() below**
},
keyboardType: TextInputType.number,
inputFormatters: [_decimal_validator],
)
),
new Flexible(
child: Text("$calculation"),
)
]
),
);
}
).toList(),
)
);
All you should need to do is use a setState to tell Flutter that the variable has changed and you want whichever Widgets that use it rebuilt:
if (text.length > 0){
setState((){
calculation = double.tryParse(text) * 23;
});
}

Gesture Detector doesn't want to size itself to a child container (unless you give it a color)

So this problem is driving me mad so any help would be greatly appreciated :)
I currently have a custom Gesture Detector which I want to size to the width and height parameters I've given to the child container. Though it will only do this if the child container is given a colour (transparent in this case). I'll post some code and screenshots down below to explain a bit further.
Things that I've tried include using SizedBox instead of a container but that didn't work. I also tried changing the values of behaviour but that didn't seem to change anything either.
//The params width and height are given values up here
...
return Container(
color: Color(0XFF0000FF), //Blue container which shows the actual size I want the GD to be.
child: ColorFiltered( //Red shows how big the GD actually is.
colorFilter: ColorFilter.mode(Color(0XFFFF0000), BlendMode.hue),
child: RawGestureDetector(
key: gestureKey,
behavior: HitTestBehavior.translucent,
gestures: <Type, GestureRecognizerFactory>{
_SingleDeviceGestureDetector: GestureRecognizerFactoryWithHandlers<
_SingleDeviceGestureDetector>(
() => _SingleDeviceGestureDetector(
//When the pointer touches down on the screen
onHorizontalDragDown: (details) { ... }
onHorizontalDragUpdate: (details) { ... }
onHorizontalDragUp: (details) { ...}
),
(_SingleDeviceGestureDetector instance) {},
),
},
child: Container( //The child container with a transparent colour.
//color: Color(0X00000000),
height: height,
width: width,
child: ...
),
),
),
);
});
}
1 Shows the widget when color is uncommented
2 Shows the widget when color is commented
When you uncomment the color field in the child container (1) the gesture detector will be the same size as the first parent container (which is what I want), and when you comment out the color field (2) the gesture detector is smaller.
Even though I have the fix, it feels very 'hacky' and it would still be good to figure out why it does this.
Cheers