How can I build 2 lined ListView.builder in Flutter - flutter

Can anyone explain me how can I build widget as ListView builder with 2 lines of items. I got an example of this type, you can check out it by the picture below:

An option is to use the Wrap widget with direction set to horizontal.
#override
Widget build(BuildContext context) {
final lorem = [
'accusamus',
'dignissimos',
'ducimus',
'blanditiis',
'praesentium',
'voluptatum'
];
return Container(
height: 100,
width: 250,
child: Wrap(
direction: Axis.horizontal,
children: List.generate(
lorem.length,
(index) => Padding(
padding: const EdgeInsets.all(8.0),
child: Text(lorem[index]),
),
),
),
);
}

This snippet should help you.
You can try it at https://dartpad.dev/028daa76945938f0e5c14aea6a8bf84b?null_safety=true
class MyWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return SizedBox(
width: 500.0,
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: ['English', 'Russian', 'Spanish']
.map((language) => LanguageButton(
language: language,
))
.toList(),
),
SizedBox(height: 20,),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: ['Some really long language', 'French', 'German']
.map((language) => LanguageButton(
language: language,
))
.toList(),
),
],
),
);
}
}
class LanguageButton extends StatelessWidget {
final String language;
const LanguageButton({Key? key, required this.language}) : super(key: key);
#override
Widget build(BuildContext context) {
return OutlinedButton(
style: OutlinedButton.styleFrom(
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(
Radius.circular(30),
),
),
),
onPressed: () {
// TODO
},
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 20.0, vertical: 8.0),
child: Text(language),
),
);
}
}
I used a Column instead of a ListView.builder. Maybe that's good enough for what you need. The gist is to have each line as a Row and set the mainAxisAlignment to spaceBetween.

Related

How to remove padding of MaterialBanner?

I want to remove the following blue padding from MaterialBanner widget, but it doesn't seem to be customizable. I want to insert an image in the red region.
I looked into MaterialBanner for using across Scaffold widgets because ScaffoldMessenger doesn't allow me to insert widgets other than MaterialBanner.
Is there any suggestion?
dartpad.
import 'package:flutter/material.dart';
void main() {
runApp(MaterialApp(home: Scaffold(body: JustBanner())));
}
class JustBanner extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return _JustBannerState();
}
}
class _JustBannerState extends State<JustBanner> {
#override
Widget build(BuildContext context) {
return Column(
children: [
ElevatedButton(
onPressed: () {
final messenger = ScaffoldMessenger.of(context);
messenger.clearMaterialBanners();
messenger.showMaterialBanner(MaterialBanner(
padding: EdgeInsets.zero,
leadingPadding: EdgeInsets.zero,
leading: const SizedBox.shrink(),
backgroundColor: Colors.blue,
content: Container(
color: Colors.red,
width: 200,
height: 50,
),
actions: const [SizedBox.shrink()]));
},
child: const Text('Banner')),
],
);
}
}
Container(
width: MediaQuery.of(context).size.width,
child: MaterialBanner(
content: Text('Hello'),
actions: [
Icon(Icons.add),
],
),
),
Its no possible without copy and re-create the class, buttonBar always appear:
final Widget buttonBar = Container( // <-- problematic widget
alignment: AlignmentDirectional.centerEnd,
constraints: const BoxConstraints(minHeight: 52.0),
padding: const EdgeInsets.symmetric(horizontal: 8),
child: OverflowBar(
overflowAlignment: widget.overflowAlignment,
spacing: 8,
children: widget.actions,
),
);
final double elevation = widget.elevation ?? bannerTheme.elevation ?? 0.0;
final Color backgroundColor = widget.backgroundColor
?? bannerTheme.backgroundColor
?? theme.colorScheme.surface;
final TextStyle? textStyle = widget.contentTextStyle
?? bannerTheme.contentTextStyle
?? theme.textTheme.bodyText2;
Widget materialBanner = Container(
margin: EdgeInsets.only(bottom: elevation > 0 ? 10.0 : 0.0),
child: Material(
elevation: elevation,
color: backgroundColor,
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Padding(
padding: padding,
child: Row(
children: <Widget>[
if (widget.leading != null)
Padding(
padding: leadingPadding,
child: widget.leading,
),
Expanded(
child: DefaultTextStyle(
style: textStyle!,
child: widget.content,
),
),
if (isSingleRow)
buttonBar, // <----- here
],
),
),
if (!isSingleRow)
buttonBar, // <----- here
if (elevation == 0)
const Divider(height: 0),
],
),
),
);

Flutter set widget position exactly center of the screen

I want to put a container widget in the middle of the device screen. However, since I used the SizedBox() and SvgPicture.asset() widgets before that, the container does not come right in the middle of the device. How can I do this?
This is my code:
class CenterWidget extends StatefulWidget {
const CenterWidget({Key? key}) : super(key: key);
#override
_CenterWidgetState createState() => _CenterWidgetState();
}
class _CenterWidgetState extends State<CenterWidget> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 30),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.max,
children: [
const SizedBox(height: 56),
SvgPicture.asset(ImageConstants.instance.logoSvg,
width: (MediaQuery.of(context).size.width - 60) / 2),
Expanded(
child: Center(
child: Container(
color: Colors.red,
width: 100,
height: 100,
),
),
),
],
),
),
),
);
}
}
Use a top center aligned stack, and put the widget you want to be centered within a Positioned.fill widget. You can put the spacer and logo in their own column to keep them arranged vertically:
class _CenterWidgetState extends State<CenterWidget> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 30),
child: Stack(
alignment: Alignment.topCenter,
children: [
Column(
children: [
const SizedBox(height: 56),
SvgPicture.asset(ImageConstants.instance.logoSvg,
width: (MediaQuery.of(context).size.width - 60) / 2),
],
),
Positioned.fill(
child: Center(
child: Container(
color: Colors.red,
width: 100,
height: 100,
),
),
),
],
),
),
),
);
}
}

Create another Widget when OnTap() is triggered

Does anyone know how to generate a new widget in Flutter when OnTap() is triggered?
In my case, I want to create a new column inside a container, when the column icon is pressed.
The icon is wrapped with an InkWell().
InkWell(
onTap: () {
print("Create Column in another Container");
},
child: Column(
children: const [
Icon(
Icons.view_agenda,
color: iconGreyColor,
size: 20.0,
),
],
),
),
The Goal shut look like this.
Container(child: Column(children:[]),)
Main part:
...widget.widgets -> spread operator .whenever ontap pressed.the array will get new element and set state will update the widget.
InkWell(
onTap: () {
setState(() {
widget.widgets.add(Container(
height: 49,
child: new Column(
children: [
Text(
"hi",
style: TextStyle(fontSize: 40),
)
],
),
));
});
print("Create Column in another Container");
},
child: Column(
children: const [
Icon(
Icons.view_agenda,
size: 20.0,
),
],
),
)
Container(
margin: EdgeInsets.only(
left: 10.0,
right: 10.0,
),
height: MediaQuery.of(context).size.height - 150,
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [...widget.widgets],
),
)
sample code
void main() => runApp(MyHomePage());
class MyHomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Page0(),
);
}
}
class Page0 extends StatefulWidget {
final widgets = [];
#override
_Page0State createState() => _Page0State();
}
class _Page0State extends State<Page0> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: ListView(
children: [
SizedBox(
height: 25.0,
),
Center(
child: Text(
"macintosh_app",
style: TextStyle(fontSize: 48),
)),
SizedBox(
height: 50.0,
),
Center(
child: InkWell(
onTap: () {
setState(() {
widget.widgets.add(Container(
height: 49,
child: new Column(
children: [
Text(
"hi",
style: TextStyle(fontSize: 40),
)
],
),
));
});
print("Create Column in another Container");
},
child: Column(
children: const [
Icon(
Icons.view_agenda,
size: 20.0,
),
],
),
),
),
SizedBox(
height: 25.0,
),
Container(
margin: EdgeInsets.only(
left: 10.0,
right: 10.0,
),
height: MediaQuery.of(context).size.height - 150,
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [...widget.widgets],
),
),
],
),
);
}
}
You can implement this using StatefulWidget(). Create a List<Widget> outside your build of StatefulWidget() and inside your InkWell() append new Widget() you want to add and call setState((){})
class YellowBird extends StatefulWidget {
const YellowBird({Key? key}) : super(key: key);
#override
State<YellowBird> createState() => _YellowBirdState();
}
class _YellowBirdState extends State<YellowBird> {
List<Widget> myWidgetList = [
Text("hello"),
];
#override
Widget build(BuildContext context) {
return Column(
children: [
InkWell(
onTap: () {
print("Create Column in another Container");
myWidgetList.add(Text("hello 2"));
setState(() {});
},
child: Column(
children: const [
Icon(
Icons.view_agenda,
size: 20.0,
),
],
),
),
Column(
children: myWidgetList,
),
],
);
}
}
You can run same code here Link
Inside OnTap(), you can pass a function that can create a Widget.
e.g.
Widget buildContainer(){ return Container(child: Text("Stack Overflow"))}
Insert this function before your build method and then call it during the OnTap() event. You can, of course, modify the Container according to your exact requirements.
The important thing to understand here is that you can create functions with any permissible return type, even a specific Widget, this is especially useful when you want to create multiple identical widgets, as it reduces redundant code.

Achieving this layout in flutter

I am trying to achieve something like this in flutter. I have a horizontal scrollable which has these rounded containers. I want the width of these containers to shrink if the elements in the scrollable is more than 3 and it should expand as per the image if the elements are less than 2. What i want is exactly like this image, i have been reading about flexible widget but when i wrap it inside a scrollable row it gives layout specific issues. Any workaround to achieve this?
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SingleChildScrollView(
padding: EdgeInsets.zero,
scrollDirection: Axis.horizontal,
child: Row(
children: [
...List.generate(
9,
(index) => Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20),
color: Colors.green,
),
height: 100,
width: 100,
),
),
)
],
)),
SizedBox(
height: 20,
),
Row(
children: [
...List.generate(
2,
(index) => Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
height: 100,
width: 180,
decoration: BoxDecoration(
color: Colors.green,
borderRadius: BorderRadius.circular(20))),
))
],
),
SizedBox(
height: 20,
),
Row(
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
height: 100,
width: 350,
decoration: BoxDecoration(
color: Colors.green,
borderRadius: BorderRadius.circular(20))),
),
],
)
],
),
),
);
}
The above build method produces the following result.(The values here are hardcoded and is just for demonstration). The list values are going to be dynamic and the desired result should be like the one in the video. How do i proceed with this?
https://streamable.com/w142je
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(debugShowCheckedModeBanner: false, home: MyHomePage());
}
}
class MyHomePage extends StatefulWidget {
final String hintText = 'hing';
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
#override
Widget build(BuildContext context) {
var list1 = List.filled(2, '2');
var list2 = List.filled(4, '4');
var list3 = List.filled(1, '1');
return SafeArea(
child: Scaffold(
body: Column(
children: [
_getList(context, list1),
_getList(context, list2),
_getList(context, list3),
],
),
),
);
}
Widget _getList(BuildContext context, List<String> list) {
bool ownSize = list.length == 2;
if (ownSize) {
return SingleChildScrollView(
padding: EdgeInsets.zero,
scrollDirection: Axis.horizontal,
child: Row(
children: list.map((t) => rowItem(t, 250)).toList(),
),
);
} else {
return Row(
children: list
.map(
(t) => Expanded(child: rowItem(t)),
)
.toList(),
);
}
}
Widget rowItem(String text, [double? width]) {
return Container(
margin: const EdgeInsets.all(8.0),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20),
color: Colors.green,
),
height: 100,
width: width,
alignment: Alignment.center,
child: Text(text),
);
}
}

Flutter listview always expands to available width

What I need is green box should end where the text ends.
Here's my code
Widget buildFlexible() {
return Flexible(
child: ListView(
shrinkWrap: true,
padding: EdgeInsets.symmetric(horizontal: 10.0, vertical: 20.0),
children: [
MessageBubble(title: 'Test1'),
MessageBubble(title: 'Test2'),
],
),
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Column(
children: <Widget>[
Row(
children: [
buildFlexible(),
],
),
],
),
);
}
class MessageBubble extends StatelessWidget {
final String title;
MessageBubble({required this.title});
#override
Widget build(BuildContext context) {
return Padding(
padding: EdgeInsets.all(10.0),
child: Material(
child: Text(title),
color: Colors.lightGreenAccent,
// elevation: 5.0,
),
);
}
}
Wrap your Padding widget from your MessageBubble class with the Wrap() widget
So, the final code for the MessageBubble should be:
class MessageBubble extends StatelessWidget {
final String title;
MessageBubble({required this.title});
#override
Widget build(BuildContext context) {
return Wrap(children: [
Padding(
padding: EdgeInsets.all(10.0),
child: Material(
child: Text(title),
color: Colors.lightGreenAccent,
// elevation: 5.0,
),
)
]);
}
}
Alternatively, you can achieve the same result with just using a Container widget and by removing the Material widget and the Padding widget as:
Wrap(children: [
Container(
padding: EdgeInsets.all(10.0),
child: Text('hi'),
color: Colors.lightGreenAccent,
)
])
Instead of:
Wrap(children: [
Padding(
padding: EdgeInsets.all(10.0),
child: Material(
child: Text(title),
color: Colors.lightGreenAccent,
// elevation: 5.0,
),
)
]);