How do I add a bullet or create a bulleted list in flutter - flutter

I have a list and I want to add a bullet to each item (I'm using new Column because I don't want to implement scrolling). How would I create a bulleted list?
I'm thinking maybe an icon but possibly there is a way with the decoration class used in the text style.

To make it as simple as possible, you can use UTF-code.
This's going to be a bullet
String bullet = "\u2022 "

Following widget will create a filled circle shape, So you can call this widget for every item in your column.
class MyBullet extends StatelessWidget{
#override
Widget build(BuildContext context) {
return new Container(
decoration: new BoxDecoration(
color: Colors.black,
shape: BoxShape.circle,
),
);
}
}
Hope this is what you want !
EDIT :
class MyList extends StatelessWidget{
#override
Widget build(BuildContext context) {
return new Column(
children: <Widget>[
new ListTile(
leading: new MyBullet(),
title: new Text('My first line'),
),
new ListTile(
leading: new MyBullet(),
title: new Text('My second line'),
)
],
);
}
}
class MyBullet extends StatelessWidget{
#override
Widget build(BuildContext context) {
return new Container(
height: 20.0,
width: 20.0,
decoration: new BoxDecoration(
color: Colors.black,
shape: BoxShape.circle,
),
);
}
}

Simple Answer
If you looking for just a symbol, then use Text('\u2022 Bullet Text')
Detailed Answer
I have created a custom widget for Bullet List of Strings. I am sharing the code so that anyone would find it helpful.
Output:
Code For BulletList Widget
(You can paste this in a separate file like 'bullet_widget.dart' and later import to your screen.)
import 'package:flutter/material.dart';
class BulletList extends StatelessWidget {
final List<String> strings;
BulletList(this.strings);
#override
Widget build(BuildContext context) {
return Container(
alignment: Alignment.centerLeft,
padding: EdgeInsets.fromLTRB(16, 15, 16, 16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: strings.map((str) {
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'\u2022',
style: TextStyle(
fontSize: 16,
height: 1.55,
),
),
SizedBox(
width: 5,
),
Expanded(
child: Container(
child: Text(
str,
textAlign: TextAlign.left,
softWrap: true,
style: TextStyle(
fontSize: 16,
color: Colors.black.withOpacity(0.6),
height: 1.55,
),
),
),
),
],
);
}).toList(),
),
);
}
}
This will Take List of Strings and Output with Bullets. Like This example.
Container(
height: 327,
decoration: BoxDecoration(
color: Constants.agreementBG,
borderRadius: BorderRadius.circular(14)),
child: SingleChildScrollView(
child: BulletList([
'Text 1',
'Text 2',
'Text 3',
]),
),
),

I used the ascii character E.G.
...your widget hierarchy
Text(String.fromCharCode(0x2022)),
...

You can just add an icon.
class MyList extends StatelessWidget{
#override
Widget build(BuildContext context) {
return new Column(
children: <Widget>[
new ListTile(
leading: Icon(Icons.fiber_manual_record),
title: new Text('My first line'),
),
new ListTile(
leading: Icon(Icons.fiber_manual_record),
title: new Text('My second line'),
)
],
);
}
}

I might be late to answer this question, but it might be of help to someone who is looking for how to use bullet in a text. It can be done using RichText.
RichText(
text: TextSpan(
text: '• ',
style: TextStyle(color: Colors.lightBlue, fontSize: 18),
children: <TextSpan>[
TextSpan(text: 'Software Developer',style:
GoogleFonts.ptSansNarrow(textStyle: TextStyle(fontSize: 18))),
],
),
)
So, in this case, the color of the bullet can also be changed as you wish!

Here you have the class for bullet text
import 'package:flutter/cupertino.dart';
class BulletText extends StatelessWidget {
late String txt;
BulletText(String t){
txt = t;
}
Widget build(BuildContext context) {
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('\u2022'),
SizedBox(width: 5),
Expanded(
child: Text(txt)
)
],
);
}
}

You can use CircleAvatar something like below
ListTile(
leading: CircleAvatar(
radius: 6.0,
backgroundColor: Colors.black,
),
title : Text("Timestamp: C0238 - Wheel Speed Mismatch")
),

I got the idea from Tushar Pol. In case you want to display a number on the bullet then you can refer to my code.
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
class AppBullet extends StatelessWidget {
AppBullet({
#required this.width,
#required this.height,
this.order,
}) : assert(width != null),
assert(height != null);
final double width;
final double height;
final int order;
#override
Widget build(BuildContext context) {
return order == null
? _buildBullet(context)
: _buildBulletWithOrder(context);
}
Widget _buildBullet(BuildContext context) {
return new Container(
height: height,
width: width,
decoration: new BoxDecoration(
color: Colors.black,
shape: BoxShape.circle,
),
);
}
Widget _buildBulletWithOrder(BuildContext context) {
return Stack(
alignment: Alignment.center,
children: [
_buildBullet(context),
Text(
'$order',
style: GoogleFonts.lato(fontSize: 12.0, color: Colors.white),
),
],
);
}
}

Entypo.dot_single from Flutter vector Icons library
import 'package:flutter/material.dart';
import 'package:flutter_vector_icons/flutter_vector_icons.dart';
class MyList extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
MyListItem(title: 'First Item'),
MyListItem(title: 'Second Item'),
],
);
}
}
class MyListItem extends StatelessWidget {
final String title;
MyListItem({this.title});
#override
Widget build(BuildContext context) {
return Row(
children: [
Icon(Entypo.dot_single),
Text(title),
],
);
}
}
Screenshot

May be this does not answer this question. I think, this answer can be helpful to other developers.
I use this code to draw a circle of solid color:
CircleAvatar(
radius: 5.0,
backgroundColor: Colors.black,
)
to add extra padding at top, I use Container:
Container(
padding: EdgeInsets.only(top: 3),
child: CircleAvatar(
radius: 5.0,
backgroundColor: Colors.black,
)
)
Also you can use other backgroundColor in CircleAvatar.
Thanks to: #NBM

The solution using flutter widget is to either use the Icon Icon(Icons.circle) or Container or CirleAvatar. There are different solutions. but the one with Icons is easier I think.
You can create a separate class to generate the bullet item that you can further easily modify as per your design. i.e you can use different bullet styles like instead of circle rectangle, triangle, any other icon.
I have just added the option to add the custom padding.
Code:
class MyBulletList extends StatelessWidget {
final String text;
final double vpad;
final double hpad;
MyBulletList({
required this.text,
this.hpad = 24.0,
this.vpad = 8.0,
});
#override
Widget build(BuildContext context) {
return Padding(
padding: EdgeInsets.symmetric(horizontal: hpad, vertical: vpad),
child: Row(
children: [
Icon(
Icons.circle,
size: 6,
color: Colors.grey,
),
SizedBox(
width: 5,
),
Text(
text,
)
],
),
);
}
}

class UL extends StatelessWidget {
final String text;
const UL(this.text, {Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 3),
child: Row(
children: [
Padding(
padding: const EdgeInsets.only(right: 14),
child: Icon(
Icons.circle,
size: Theme.of(context).textTheme.bodyText1?.fontSize,
),
),
Text(text, style: Theme.of(context).textTheme.bodyText1),
],
),
);
// return ListTile(
// contentPadding: EdgeInsets.zero,
// minVerticalPadding: 0,
// dense: true,
// visualDensity: VisualDensity(vertical: -4, horizontal: 0),
// leading: Container(
// height: double.infinity,
// child: Icon(
// Icons.circle,
// size: Theme.of(context).textTheme.bodyText1?.fontSize,
// ),
// ),
// title: Text(text, style: Theme.of(context).textTheme.bodyText1),
// );
}
}
You can also pass in padding as an optional parameter to this widget if needed to customize padding

Related

How do i switch Appbar title and child content with state-management in flutter?

I have a problem i have been struggling to get done for a day now
I want to dynamically switch appbar from this :
to this :
when a button is pressed.
The button is situated in the scaffold bottomNavigationBar of the first appbar widget.
I will give the code snippet of this particular widget.
I tried creating an entirely different widget and set the button onTap function to route to the new widget created.
This is not a suitable solution for me as i wish to just change state of the appbar as to avoid the weird transition when changing pages.
Also please note that the second image has a leading button that would enable the user to go back to the previous appbar.
How do i achieve this?
THIS IS THE CODE SNIPPET
import 'package:flutter/material.dart';
class CustomersView extends StatefulWidget {
#override
State<CustomersView> createState() => _CustomersViewState();
}
class _CustomersViewState extends State<CustomersView> {
List<String> items = [
"All",
"Inactive",
"One time",
"Loyal",
"Active",
];
int current = 0;
List<DropdownMenuItem<String>> get dropdownItems {
List<DropdownMenuItem<String>> menuItems = [
DropdownMenuItem(
child: Text(
"Today",
),
value: "Today"),
];
return menuItems;
}
#override
Widget build(BuildContext context) {
//final controller = Get.put(EServicesController());
return Scaffold(
appBar: AppBar(
toolbarHeight: 60,
backgroundColor: Colors.white,
title: Text(
"Customers".tr,
style: GoogleFonts.poppins(
color: Color(0xff000000),
fontSize: 20,
fontWeight: FontWeight.w600),
),
actions: [
SearchButtonWidget(),
SettingsButtonWidget(),
],
centerTitle: false,
elevation: 0,
automaticallyImplyLeading: false,
leadingWidth: 15,
// leading: new IconButton(
// icon: new Icon(Icons.arrow_back_ios, color: Color(0xff3498DB)),
// onPressed: () => {Get.back()},
// ),
),
body: RefreshIndicator(
onRefresh: () async {
// Get.find<LaravelApiClient>().forceRefresh();
// await controller.refreshNotifications(showMessage: true);
// Get.find<LaravelApiClient>().unForceRefresh();
},
child: ListView(
primary: true,
children: <Widget>[
mainHeader(),
SizedBox(
height: 10,
),
CustomersCategoriesBuilder(current: current),
],
),
),
//floatingActionButtonLocation: FloatingActionButtonLocation.endFloat,
bottomNavigationBar: current == 0 ? SizedBox() : MessageCustomersButton(),
);
}
//Button that controls the appbar state
class MessageCustomersButton extends StatelessWidget {
const MessageCustomersButton({
Key key,
this.value = false,
}) : super(key: key);
final bool value;
#override
Widget build(BuildContext context) {
return Container(
color: Colors.white,
child: Padding(
padding: const EdgeInsets.all(20.0),
child: FadeInDown(
child: MaterialButton(
onPressed: () {
//this is the new page route ( unsatisfied approach )
Get.toNamed(Routes.MESSAGE_CUSTOMERS);
},
color: Color(0xff34495E),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20.18),
),
padding: EdgeInsets.symmetric(horizontal: 30, vertical: 10),
minWidth: double.infinity,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.chat,
size: 18,
color: Colors.white,
),
SizedBox(
width: 10,
),
Text(
'Message Customers',
style: GoogleFonts.poppins(
color: Colors.white,
fontSize: 16,
fontWeight: FontWeight.w600),
),
],
),
),
),
),
);
}
}
Try creating the widget for AppBar only and handle the different states of AppBar there only by passing a flag like isSecondStyleAppBar then in your CustomersView widget, handle the flag using setState
class CustomAppBar extends StatelessWidget {
final bool isSecondStyleAppBar;
const CustomAppBar(this.isSecondStyleAppBar, {Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return const AppBar();
}
}

How do I fix my problem with routes in Flutter?

good evening. I am currently doing a To-do List in Flutter and I want to pass the Title of my List and the Description of my List when I click on a new screen but upon setting up Routes and and declaring the values on my next, it shows the "2 positional arguments expected, but 0 found" on the routes I've set up. Here are my codes:
Here is my 1st screen:
import 'package:flutter/material.dart';
import 'package:todo_list/details.dart';
import 'package:todo_list/note.dart';
class MyApp extends StatelessWidget {
final String text;
final int number;
final String listDescription;
const MyApp(
{super.key,
required this.text,
required this.number,
required this.listDescription});
#override
Widget build(BuildContext context) {
return MaterialApp(
routes: {
DetailsPage.routeName: (ctx) => DetailsPage(),
},
home: CustomListTile(
text: text,
number: number,
listDescription: listDescription,
),
);
}
}
class CustomListTile extends StatelessWidget {
final String text;
final int number;
final String listDescription;
const CustomListTile(
{super.key,
required this.text,
required this.number,
required this.listDescription});
#override
Widget build(BuildContext context) {
return InkWell(
onTap: () {
Navigator.pushNamed(context, DetailsPage.routeName,
arguments: Note(title: text, description: listDescription));
},
/* onTap: () {
Widget okButton = TextButton(
child: const Text("CLOSE"),
onPressed: () {
Navigator.of(context).pop();
},
);
AlertDialog alert = AlertDialog(
title: Text(text),
content: Text('This item in the list contains $listDescription'),
actions: [
okButton,
]);
showDialog(
context: context,
builder: (BuildContext context) {
return alert;
});
}, */
child: Padding(
padding: const EdgeInsets.only(left: 20.0, right: 20.0, top: 20.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text("$number. $text",
style: const TextStyle(
fontSize: 20,
)),
const Icon(Icons.chevron_right)
],
),
Text(
listDescription,
style: const TextStyle(fontSize: 14, color: Colors.grey),
),
const Divider()
],
),
),
);
}
}
and here is my 2nd screen:
import 'package:flutter/material.dart';
import 'note.dart';
class DetailsPage extends StatefulWidget {
static const String routeName = "/details";
final String text;
final String listDescription;
const DetailsPage(this.text, this.listDescription, {super.key});
#override
State<DetailsPage> createState() => _DetailsPageState();
}
class _DetailsPageState extends State<DetailsPage> {
late Note params;
#override
void didChangeDependencies() {
params = ModalRoute.of(context)!.settings.arguments! as Note;
super.didChangeDependencies();
}
#override
Widget build(BuildContext context) {
Widget titleSection = Container(
padding: const EdgeInsets.all(32),
child: Row(
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
padding: const EdgeInsets.only(bottom: 0),
child: Text(
params.title,
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 25,
),
),
),
],
),
),
],
),
);
Color color = Theme.of(context).primaryColor;
Widget buttonSection = Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
_buildButtonColumn(
color,
Icons.edit,
'EDIT',
),
_buildButtonColumn(color, Icons.delete, 'DELETE'),
],
);
Widget textSection = Padding(
padding: const EdgeInsets.all(20),
child: Text(
params.description,
softWrap: true,
),
);
return MaterialApp(
title: 'Layout for a New Screen',
theme: ThemeData(
primarySwatch: Colors.brown,
),
home: Scaffold(
appBar: AppBar(
leading: IconButton(
icon: const Icon(Icons.arrow_back),
onPressed: () {
Navigator.pop(context);
},
),
title: Text(params.title),
),
body: ListView(
children: [
Image.asset(
'lib/images/placeholder.jpg',
width: 600,
height: 240,
fit: BoxFit.cover,
),
titleSection,
buttonSection,
textSection,
],
),
),
);
}
Column _buildButtonColumn(
Color color,
IconData icon,
String label,
) {
return Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(icon, color: color),
Container(
margin: const EdgeInsets.only(top: 8),
child: Text(
label,
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w400,
color: color,
),
),
),
],
);
}
}
/* return Scaffold(
appBar: AppBar(title: Text(text)),
body: Center(
child: Row(
children: [Text(description)],
),
));
}
} */
How do I make it so that the data I'll pass such as the Title and the Description will appear on the 2nd screen without the error "2 positional argument(s) expected, but 0 found.
Try adding the missing arguments." appearing.
I tried the Quick Fixes on VS Code such as adding a const modifier but I think the const modifier wouldn't do a fix since both data I'm trying to pass are dynamic and may change from time to time.
As you've define details page
class DetailsPage extends StatefulWidget {
static const String routeName = "/details";
final String text;
final String listDescription;
const DetailsPage(this.text, this.listDescription, {super.key});
You need to pass two string as positional argument.
So it can be
routes: {
DetailsPage.routeName: (ctx) => DetailsPage("text","description"),
},
also while you are using route arguments, you can remove theses from widget class and just accept from state class context with ModalRoute.
You can check this example and development/ui/navigation .

how to add two posts per screen flutter

I'm trying to create a video screen like the picture. on the right side picture showing my implementation so far. how can I create video screen like half of the screen and one after the other as left UI below. (two videos per screen). appriciate your help on this. I haveadded my code for your refernce
post_template.dart
import 'package:flutter/material.dart';
import '../constants/button.dart';
class PostTemplate extends StatelessWidget {
final String username;
final String videoDescription;
final String numberOfLikes;
final String numberOfComments;
final String numberOfShares;
final userPost;
PostTemplate({
required this.username,
required this.videoDescription,
required this.numberOfLikes,
required this.numberOfComments,
required this.numberOfShares,
required this.userPost,
});
#override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: [
// user post (at the very back)
userPost,
// user name and caption
Padding(
padding: const EdgeInsets.all(20.0),
child: Container(
alignment: Alignment(-1, 1),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.end,
children: [
Text('#' + username,
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16,
)),
SizedBox(
height: 10,
),
RichText(
text: TextSpan(
children: [
TextSpan(
text: videoDescription,
style: TextStyle(color: Colors.white)),
TextSpan(
text: ' #live #lalaive',
style: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.white)),
],
),
)
],
),
),
),
// buttons
Padding(
padding: const EdgeInsets.all(20.0),
child: Container(
alignment: Alignment(1, 1),
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: [
MyButton(
icon: Icons.people,
number: numberOfComments,
),
MyButton(
icon: Icons.thumb_up,
number: numberOfLikes,
),
MyButton(
icon: Icons.share,
number: numberOfShares,
),
],
),
),
)
],
),
);
}
}
video_screen.dart
import 'package:flutter/material.dart';
import 'package:lala_live/screens/post_template.dart';
class VideoScreen extends StatefulWidget {
const VideoScreen({Key? key}) : super(key: key);
#override
_VideoScreenState createState() => _VideoScreenState();
}
class _VideoScreenState extends State<VideoScreen> {
final _controller = PageController(initialPage: 0);
#override
Widget build(BuildContext context) {
return Scaffold(
body: PageView(
controller: _controller,
scrollDirection: Axis.vertical,
children: [
MyPost1(),
MyPost2(),
MyPost3(),
],
),
);
}
}
class MyPost1 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return PostTemplate(
username: 'amandasharma',
videoDescription: 'Free your mind',
numberOfLikes: '1.2M',
numberOfComments: '1232',
numberOfShares: '122',
userPost: Container(
//color: Colors.deepPurple[300],
decoration: new BoxDecoration(
image: new DecorationImage(
image: new AssetImage("asset/images/girl.jpeg"),
fit: BoxFit.fill,
)
)
)
);
}
}
class MyPost2 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return PostTemplate(
username: 'zuckerberg',
videoDescription: 'reels for days',
numberOfLikes: '1.2M',
numberOfComments: '232',
numberOfShares: '122',
userPost: Container(
decoration: new BoxDecoration(
image: new DecorationImage(
image: new AssetImage("asset/images/nature.jpg"),
fit: BoxFit.fill,
)
)
),
);
}
}
class MyPost3 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return PostTemplate(
username: 'randomUser',
videoDescription: 'Free your mind',
numberOfLikes: '1.2B',
numberOfComments: '232',
numberOfShares: '122',
userPost: Container(
color: Colors.blue[300],
),
);
}
}
for MyPost3() you can wrap the containers in a column and use the Expanded widget.
class MyPost3 extends StatelessWidget {
const MyPost3({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return PostTemplate(
username: 'randomUser',
videoDescription: 'Free your mind',
numberOfLikes: '1.2B',
numberOfComments: '232',
numberOfShares: '122',
userPost: Column(
children: [
Expanded(
child: Container(
color: Colors.blue[300],
),
),
Expanded(
child: Container(
color: Colors.red[300],
),
),
],
),
);
}
}
I believe this is where you want the video widgets to appear ,
// user post (at the very back)
userPost,
You can put the widgets in a column and use Expandable to give them size, i.e flex 2 for each to split screen halfway.
Column -
- Expanded( flex:2, child:video1
- Expanded( flex:2, child:video2
On MyPost3():
return PostTemplate(
username: 'randomUser',
videoDescription: 'Free your mind',
numberOfLikes: '1.2B',
numberOfComments: '232',
numberOfShares: '122',
userPost: SafeArea(
child: Column(children: [
Expanded(
child: Container(
color: Colors.blue,
)),
Expanded(
child: Container(
color: Colors.red,
))
]),
));
You can use an Expanded() to take as much space you can, surrounded by a SafeArea() to ensure your screens don't overlap with your status bar leaving the possibility of an overflow.

Make Text widget shrink to longest line when text is wrapped to multiple lines

Is it possible to shrink a Text widget to the width of the longest line when the text is wrapped because it becomes too long to fit on a single line?
Basically I have this custom widget:
class MyWidget extends StatelessWidget {
final String text1;
final String text2;
const MyWidget({Key? key, this.text1='', this.text2=''}) : super(key: key);
#override
Widget build(BuildContext context) {
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Container(
color: Colors.lightBlue,
child: Center(child: Text(text1))
)
),
Expanded(
child: Container(
color: Colors.lightGreen,
alignment: Alignment.centerRight,
child: Text(text2, textAlign: TextAlign.start)
)
)
]
);
}
}
Here's what it looks like with one short and one long text:
But what I want is this:
Note that I do not want the text to be right-aligned - the wrapped lines should be aligned to the left, but the entire Text widget should shrink to the longest line.
Just found the answer myself when investigating the more exotic properties of the Text widget. Apparently, textWidthBasis: TextWidthBasis.longestLine does exactly what I want.
class MyWidget extends StatelessWidget {
final String text1;
final String text2;
const MyWidget({Key? key, this.text1='', this.text2=''}) : super(key: key);
#override
Widget build(BuildContext context) {
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Container(
color: Colors.lightBlue,
child: Center(child: Text(text1))
)
),
Expanded(
child: Container(
color: Colors.lightGreen,
alignment: Alignment.centerRight,
child: Text(text2, textWidthBasis: TextWidthBasis.longestLine)
)
)
]
);
}
}
you can wrap Text with FittedBox
FittedBox(
fit: BoxFit.fitWidth,
child: Text(
"some text"
),),
This answered for me. You can replace the following part in your code:
Expanded(
child: Container(
color: Colors.lightGreen,
alignment: Alignment.centerRight,
child: LayoutBuilder(builder: (context, size) {
final span = TextSpan(text: text2);
final tp = TextPainter(
text: span,
maxLines: 1,
textDirection: Directionality.of(context));
tp.layout(maxWidth: size.maxWidth);
if (tp.didExceedMaxLines) {
return Padding(
padding: const EdgeInsetsDirectional.only(start: 16.0),
child: Text(text2),
);
} else {
return Text(text2);
}
})))

How to replicate (this) specific Android constraint layout on Flutter

I want to find a solution to mimic attached image Android contraint layout on a Flutter wrapped widgets structure.
In this case, blue text TextView width is wrap content and image (dollar sign) must take same width.
Any suggestions? Thanks a lot!
This is my current code, a simple column, I tried a lot of alternatives but I can't figure it out logically :/
Widget _getPublisherLogoImage() {
return ClipOval(
child: CachedNetworkImage(
imageUrl: _publisherLogo,
placeholder: (context, url) => CircularProgressIndicator(),
errorWidget: (context, url, error) => Icon(Icons.error),
width: 50,
height: 50,
),
);
}
Widget _getNewsItemHour(BuildContext context) {
return Padding(
padding: EdgeInsets.only(top: 8),
child: Text(DateUtils.getHoursMinutesStringDate(_timestamp),//'09:35',
style: TextStyle(
fontSize: 14,
color: Theme.of(context).primaryColor,
fontWeight: FontWeight.bold))
);
}
Widget _getNewsItemQuotation() {
return Padding(
padding: EdgeInsets.only(top: 8),
child: Text('\$$_price',
style: TextStyle(
fontSize: 18,
color: Colors.black,
fontWeight: FontWeight.bold))
);
}
.
.
.
Column(
children: <Widget>[
_getPublisherLogoImage(),
_getNewsItemHour(context),
_getNewsItemQuotation()
],
)
.
.
.
To match the text to the image, I use a technique where an IntrinsicWidth widget wraps a Column, whose crossAxisAlignment is stretched.
Change the image width in the code from width: 200 to whatever you want, and the text underneath will match its size.
P.S. The second text widget at the bottom of the screen scales to the screen size.
Code:
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Replicate Android Layout',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Replicate Android Layout')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
IntrinsicWidth(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
_getPublisherLogoImage(),
FittedBox(
fit: BoxFit.fitWidth, child: _getNewsItemHour(context)),
],
),
),
_getNewsItemQuotation(context),
],
),
),
);
}
}
Widget _getPublisherLogoImage() {
return Image(
image: NetworkImage(
'https://flutter.github.io/assets-for-api-docs/assets/widgets/owl.jpg'),
width: 200,
);
}
Widget _getNewsItemHour(BuildContext context) {
return Padding(
padding: EdgeInsets.only(top: 10),
child: Text('09.35',
style: TextStyle(
color: Theme.of(context).primaryColor,
fontWeight: FontWeight.normal)));
}
Widget _getNewsItemQuotation(BuildContext context) {
double width = MediaQuery.of(context).size.width;
double _price = 60.59;
return Text(
'\$$_price',
style: TextStyle(
fontSize: width * 0.25,
color: Colors.grey,
),
);
}
Run in dartpad.