Queries on Flutter ListView - flutter

Im learning about listviews and I have the below two dart files, one using ListView builder and the other Listview. Both output the same result. I have been following the listview guide: https://pusher.com/tutorials/flutter-listviews
Below are my queries on listview:
I understand in the real world the data will be coming from an API and wanted to know which of the below options will be used and why?
Am i correct to understand that any widget like container, text can be child of within a listView?
In option 1 the ListView child is a function _buildListItemsFromLocation(). Is this a good practise or should we move the _buildListItemsFromLocation()code to a separate dart file?
Option1: ListView
class LocationListView extends StatefulWidget {
#override
_LocationListViewState createState() => _LocationListViewState();
}
class _LocationListViewState extends State<LocationListView> {
List<Container> _buildListItemsFromLocation() {
int index = 0;
return locationData.map((location) {
var container = Container(
child: Row(
children: [
Container(
margin: EdgeInsets.all(10.0),
child: Image(
image: AssetImage(location.imagePath),
width: 100.0,
height: 100.0,
fit: BoxFit.cover,
),
),
Container(
child: Text(location.name),
)
],
),
);
return container;
}).toList();
}
#override
Widget build(BuildContext context) {
return ListView(
children: _buildListItemsFromLocation(),
);
}
}
Option 2 - ListView.builder
class LocationList extends StatefulWidget {
#override
_LocationListState createState() => _LocationListState();
}
class _LocationListState extends State<LocationList> {
#override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: locationData.length,
itemBuilder: (context, index) {
return Row(
children: [
Container(
margin: EdgeInsets.all(10.0),
child: Image(
image: AssetImage(locationData[index].imagePath),
width: 100.0,
height: 100.0,
fit: BoxFit.cover,
),
),
Container(
child: Text(locationData[index].name),
)
],
);
}
);
}
}

I use method 2 because it is easy to understand and follows the order from top to bottom so it is easy to read the code.
Any widget can be a child of another widget. Depending on how and what you use them for.
Many people say that we should create another class and then call it again rather than split as above because it affects the performance of the app. In case of using a lot but only in one screen, you can use the same method as your own.
The answer may be flawed, have nothing to give yourself.

If you don't know in advance list size, then create it through builder
1.1 If you create list and you know that elements count won't be more than ten or
twelve, you can create ListView from example1
Any widget can be in ListView. For convenience there is widget called
ListTile, which contains leading, trailing, title, subtitle widgets
its's ok

Related

Too many postival arguments when adding hero transition, image, and text

import 'package:flutter/material.dart';
class DetailsScreen extends StatefulWidget {
final int index;
const DetailsScreen({Key? key, required this.index}) : super(key: key);
#override
State<DetailsScreen> createState() => _DetailsScreenState();
}
class _DetailsScreenState extends State<DetailsScreen> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Center(
child: Hero(
tag: widget.index,
child: Image.network(
"https://raw.githubusercontent.com/markknguyen/pictures/master/pic/${widget.index + 1}.png",
),
const Text("Rome"),
),
),
);
}
}
I tried adding const thinking it will resolve the issue but I didn't. The code did not run. I Just wanted to add some sort of text box in a page. const Text("Rome"), is the main concern.
You can't just have your Text widget there with no parent. You need to put your Hero and Text widget in a Column like so:
Center(
child: Column(
children: [
Hero(
tag: widget.index,
child: Image.network(
"https://raw.githubusercontent.com/markknguyen/pictures/master/pic/${widget.index + 1}.png",
),
),
Text("Rome"),
],
),
),
or any other Widget that acceptes multiple children such as Row or ListView based on your needs
The issue is that your Text() widget isn't passed as a parameter. Currently, the code can't compile due to treating your Text("Rome") as a 'mistake', sort of speaking.
Depending on your use-case scenario, you can either use:
a Column() - if you want your widgets to be one after another in a column.
a Stack() - if you want your widgets to be placed one under another.
You'll have to pass the children attribute to both, so for example:
Column(
children: [
Image.network(
"https://raw.githubusercontent.com/markknguyen/pictures/master/pic/${widget.index + 1}.png",
),
const Text("Rome"),
]
)

Resizing parent widget to fit child post 'Transform' in Flutter

I'm using Transforms in Flutter to create a scrolling carousel for selecting from various options.
This uses standard elements such as ListView.builder, which all works fine, aside from the fact that the parent widget of the Transform doesn't scale down to fit the content as seen here:
Here's the code used to generate the 'card' (there was actually a Card in there, but I've stripped it out in an attempt to get everything to scale correctly):
return Align(
child: Transform(
alignment: Alignment.center,
transform: mat,
child: Container(
height: 220,
color: color,
width: MediaQuery.of(context).size.width * 0.7,
child: Text(
offset.toString(),
style: TextStyle(color: Colors.white, fontSize: 12.0),
),
),
),
);
}
Even if I remove the 'height' parameter of the Container (so everything scales to fit the 'Text' widget), the boxes containing the Transform widgets still have the gaps around them.
Flutter doesn't seem to have any documentation to show how to re-scale the parent if the object within is transformed - anyone here knows or has any idea of a workaround?
EDIT: The widget returned from this is used within a build widget in a Stateful widget. The stack is Column > Container > ListView.builder.
If I remove the Transform, the Containers fit together as I'd like - it seems that performing a perspective transform on the Container 'shrinks' it's content (in this case, the color - check the linked screen grab), but doesn't re-scale the Container itself, which is what I'm trying to achieve.
I have a tricky solution for this: addPostFrameCallback + overlay.
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
// ignore: must_be_immutable
class ChildSizeWidget extends HookWidget {
final Widget Function(BuildContext context, Widget child, Size size) builder;
final Widget child;
final GlobalKey _key = GlobalKey();
OverlayEntry _overlay;
ChildSizeWidget({ this.child, this.builder });
#override
Widget build(BuildContext context) {
final size = useState<Size>(null);
useEffect(() {
WidgetsBinding.instance.addPostFrameCallback((timestamp) {
_overlay = OverlayEntry(
builder: (context) => Opacity(
child: SingleChildScrollView(
child: Container(
child: child,
key: _key,
),
),
opacity: 0.0,
),
);
Overlay.of(context).insert(_overlay);
WidgetsBinding.instance.addPostFrameCallback((timestamp) {
size.value = _key.currentContext.size;
_overlay.remove();
});
});
return () => null;
}, [child]);
if (size == null || size.value == null) {
return child;
} else {
return builder(context, child, size.value);
}
}
}
Usage:
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
class HomeView extends HookWidget {
#override
Widget build(BuildContext context) {
final change = useState<bool>(false);
final normal = Container(
color: Colors.blueAccent,
height: 200.0,
width: 200.0,
);
final big = Container(
color: Colors.redAccent,
height: 300.0,
width: 200.0,
);
return Column(
children: [
Container(
alignment: Alignment.center,
child: ChildSizeWidget(
child: change.value ? big : normal,
builder: (context, child, size) => AnimatedContainer(
alignment: Alignment.center,
child: SingleChildScrollView(child: child),
duration: Duration(milliseconds: 250),
height: size.height,
),
),
color: Colors.grey,
),
FlatButton(
child: Text('Toggle child'),
onPressed: () => change.value = !change.value,
color: Colors.green,
),
],
);
}
}
I have a menu with several options, they have different height and with the help of the animations this is ok, it's working really nice for me.
Why are you using Align, as much as I can see in your code, there is no property set or used, to align anything. So try removing Align widget around Transform.
Because according to the documentation, Transform is such a widget that tries to be the same size as their children. So that would satisfy your requirement.
For more info check out this documentation: https://flutter.dev/docs/development/ui/layout/box-constraints
I hope it helps!

RenderListWheelViewport object was given an infinite size during layout

I am using ListWheelScrollView Widget to give a wheeling effect to my list item but getting the error as mentioned. I just want to show Stacked Items with some image and texts in individual list item and give a 3D Wheeling effect to them.
Below is my code ->
class ExploreWidget extends StatefulWidget {
#override
State<StatefulWidget> createState() => _ExploreState();
}
class _ExploreState extends State<ExploreWidget> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: null,
body: Column(
children: <Widget>[
_header(),
_exploreList()
],
)
);
}
Widget _header(){
return SizedBox(
height: 200,
width: 800,
);
}
Widget _exploreList(){
return ListWheelScrollView.useDelegate(
itemExtent: 75,
childDelegate: ListWheelChildBuilderDelegate(
builder:(context,index){
return Container(
height: 500,
width: 800,
child: Stack(
children: <Widget>[
Image(image: AssetImage(
_products[index].image
)),
Text(_products[index].name,style: Style.sectionTitleWhite,),
Text('70% off',style: Style.cardListTitleWhite,),
],
),
);
}
),
);
}
}
The error was occuring due to the way _exploreList() widget is implemented. This widget is wrapped inside Column which doesn't scroll in itself. Moreover, you are returning a ScrollView that has an infinite size. Hence it was throwing the said error. To resolve this issue, wrap _exploreList() widget inside Flexible which takes only minimum available space to render and scroll. Working sample code below:
body: Column(
children: <Widget>[
_header(),
Flexible(
child: _exploreList()
)
],
)
Now you should be able to use WheelScrollView properly.

Move, zoom and resize Positioned widget inside Stack widget in Flutter

I would like to be able to move, rotate and zoom every element that you see in the image: 3 pictures and 1 text for example.
Those elements are Positioned widgets (the red boxes) inside a Stack widget.
I'm trying to use the package matrix_gesture_detector (https://pub.dev/packages/matrix_gesture_detector), but the problem is that I can't perform the given actions on the Positioned and I can't wrap it inside any other widget (like MatrixGestureDetector for example) that handles all actions, because "Positioned widgets must be placed directly inside Stack widgets".
If I use MatrixGestureDetector as a child of the Positioned I'm able to perform all the actions, but only inside the Positioned boundaries
How can I perform those actions directly on the Positioned? Or can I use some other widget instead of Stack/Positioned?
For me it worked pretty well.. Try something like this:
First i made a widget so that each widget can have its own Transformer Matrix
class TransformerWidget extends StatefulWidget {
final Widget child;
TransformerWidget(this.child, {Key key}) : super(key: key);
#override
_TransformerWidgetState createState() => _TransformerWidgetState();
}
class _TransformerWidgetState extends State<TransformerWidget> {
final ValueNotifier<Matrix4> notifier = ValueNotifier(Matrix4.identity());
#override
Widget build(BuildContext context) {
final ValueNotifier<Matrix4> notifier = ValueNotifier(Matrix4.identity());
return MatrixGestureDetector(
onMatrixUpdate: (m, tm, sm, rm) {
notifier.value = m;
},
child: AnimatedBuilder(
animation: notifier,
builder: (ctx, child) {
return Transform(
transform: notifier.value,
child: widget.child,
);
},
),
);
}
}
Secondly i wrapped the widget on Stack like this:
Stack(
children: [
TransformerWidget(
Container(
color: Colors.white30,
),
),
Positioned.fill(
child: Container(
transform: notifier.value,
child: TransformerWidget(
FittedBox(
fit: BoxFit.contain,
child: Icon(
Icons.favorite,
color: Colors.deepPurple.withOpacity(0.5),
),
),
),
),
),
TransformerWidget(
Container(
decoration: FlutterLogoDecoration(),
alignment: Alignment(0, -0.5),
child: Text(
'use your two fingers to translate / rotate / scale ...',
style: Theme.of(context).textTheme.display2,
textAlign: TextAlign.center,
),
),
),
It worked great! Except that if you pinch or something touching two of the widgets, both get transformed.. Still do not know how to fix this, but it works for now! :D

How to align widget to another widget in Flutter?

I have a RaisedButton widget inside of a Center widget as one of the widgets in a Column of widgets. I want to add a CircularProgressIndicator to the right side of this button and show it when the button is pressed. Yet I want to leave the button centred when the progress bar is shown. In other words I want the button always be in the center and the progress bar aligned to this button.
I tried to use a Row here but this pushes the button and it becomes not centred any more.
EDIT1: Looking at the result of the solution provided by #Anil Chauhan (thanks for the answer):
Like I said before that I tried to use Row like he did, the problem is that in this case the button is not in the centred in the screen and is pushed by the progress bar. And I need it to stay in the middle of it's row.
EDIT2: #Anil Chauhan edited answer now works for a specific case in which the button is predetermined size. But if the size of the button is changed based on the language of the text (in apps that have several languages) this solution will not work.
This is the reason the question I asked is: "How to align widget to another widget". Because if I could that I don't have to worry about the button text size any more.
What would be the right way to handle this in Flutter?
class MyPage extends StatefulWidget {
#override
_MyPageState createState() => _MyPageState();
}
class _MyPageState extends State<MyPage> {
bool _showIndicator = false;
void _onButtonClicked() {
setState(() {
_showIndicator = !_showIndicator;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Row(
children: <Widget>[
const Expanded(child: SizedBox()),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 10.0),
child: RaisedButton(
child: Text("I am Too Big"),
onPressed: _onButtonClicked,
),
),
Expanded(
child: _showIndicator
? const Align(
alignment: Alignment.centerLeft,
child: CircularProgressIndicator(),
)
: const SizedBox(),
),
],
),
),
);
}
}
Here is my explanation:
The RaisedButton size is depends on its child. If you add it to Row it will automatically align to left(or start).
Expanded widget will fill the remaining space in Flex widget(Row & Column are child classes of Flex). If you add more than one Expanded widgets, it will split equally. So I added two Expanded to both the side of button to make it center.
Now We should give child for Expanded Widget.
For the first one(left) we don't have anything to display so I added SizedBox.
For the second one(right) we need CircularProgressIndicator. so I added it.
The Expanded widget will try to make its child to fill the space inside of it. So the CircularProgressIndicator will become Ellipse shaped. We can avoid this by using Align Widget.
Try this:
Updated:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: MyAppOne(),
);
}
}
class MyAppOne extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyAppOne>{
bool show = false;
#override
Widget build(BuildContext context){
return Scaffold(
body: Stack(
alignment: Alignment.center,
children: <Widget>[
Container(
alignment: Alignment.center,
child: RaisedButton(
onPressed: () {
setState(() {
show =!show;
});
},
child: Text('Show'),
),
),
Positioned(
right: MediaQuery.of(context).size.width * .20,
child: Container(
alignment: Alignment.center,
padding: EdgeInsets.all(10.0),
child: show ? CircularProgressIndicator() : Container(),
),
)
],
)
);
}
}
Flutter's Column and Row widgets have two convenient properties called mainAxisAlignment and crossAxisAlignment. I assume since you're using a Column and want the CircularProgressIndicator to the right of the button, you might be want to use crossAxisAlignment since the cross-axis of a Column is along the horizontal.
If possible, please share your code for better understanding and support of the issue.