how to set width of container based on text length in flutter - flutter

I have created list of cateogories using list listview.builder
here I want to highlight selected category by underlining category text with container and I want to apply width based on text length...same way like we do underline for text,
I know they are inbuilt some packages but I don't want to use it as I want to implement my logic.
here is my code
I have set comment where I want to dynamic width
class CatogoryList extends StatefulWidget {
#override
State<CatogoryList> createState() => _CatogoryListState();
}
class _CatogoryListState extends State<CatogoryList> {
List<String> categories=['HandBag','Jwellery','FootWear','Dresses','Pens','Jeans','Trousers'];
int selectedindex=2;
#override
Widget build(BuildContext context) {
return SizedBox(
height: 30,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: categories.length,
itemBuilder: (context,index){
return buildCategory(index);
}),
);
}
Widget buildCategory(int index)
{
return GestureDetector(
onTap: (){
setState(() {
selectedindex=index;
});
},
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(categories[index],style: TextStyle(fontSize: 20,color: selectedindex==index?Colors.blue:Colors.grey),),
if(selectedindex==index)
Container(
// here I want to set widget of container based on text length
height: 3,width: 30,color: Colors.blue,),
],),
),
);
}
}

One way is to wrap the Column in an IntrinsicWidth and leave out the width from the Container. Like
Widget buildCategory(int index)
{
return GestureDetector(
onTap: (){
setState(() {
selectedindex=index;
});
},
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: IntrinsicWidth(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(categories[index],style: TextStyle(fontSize: 20,color: selectedindex==index?Colors.blue:Colors.grey),),
if(selectedindex==index)
Container(height: 3,color: Colors.blue,),
],),
),
),
);
}

I think the proper approach would be to wrap the selected Text widget with Container and apply the decoration there.
I'm answering your question as well:
final TextPainter textPainter = TextPainter(
text: TextSpan(text: text, style: yourTextStyle), /// apply your style here
textScaleFactor: MediaQuery.of(context).textScaleFactor,
textDirection: TextDirection.ltr,
)..layout();
final double width = textPainter.size.width;

Related

Wrap widget doesn't prevent overflow when using accessibility options

I have a very particular question. I'm coding a cryptogram app and I'm trying to figure out how to accommodate for visibility as best I can. I have the Wrap widget functioning fine when the font is the standard size (as set in the system) but if I crank the font size way up (like a friend of mine sometimes has to do) then the widgets overflow. I currently have each cryptogram broken down into an array of strings, and each string is further broken down into an array of character display widgets, so the children of the wrap are a list of word widgets that are, in turn, a list of these character widgets. I'll post code below. But what I am trying to achieve is the same kind of wrapping as is happening now, because there are 260 quotes so programming a layout for each one is out of the question, but also allow for side to side scrolling when the font is much larger than normal. I've also attached screenshots to illustrate what is happening.
Expanded(
flex: 5,
child: Scrollbar(
child: SingleChildScrollView(
child: Container(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: Wrap(
spacing: 8.0,
runSpacing: 8.0,
children: _createWordWidgets(),
),
),
),
),
),
),
List<Widget> _createWordWidgets() {
List<Widget> list = List();
for (int i = 0; i < gameManager.getWords().length; i++) {
list.add(WordWidget(
word: gameManager.getWords()[i],
selection: selection,
));
}
return list;
}
List<String> getWords() {
return _cipherHelper.getWordList();
}
void _makeWordList() {
_words = _cryptogram.split(' ').toList();
}
List<String> getWordList() {
return _words;
}
class WordWidget extends StatefulWidget {
WordWidget({this.word, this.selection});
final String word;
final String selection;
#override
_WordWidgetState createState() => _WordWidgetState();
}
class _WordWidgetState extends State<WordWidget> {
List<String> characters;
List<LetterWidget> letterWidgets = List();
CipherHelper brain = CipherHelper.getInstance();
#override
Widget build(BuildContext context) {
characters = widget.word.split('');
return Padding(
padding: EdgeInsets.symmetric(
horizontal: 4.0,
),
child: Container(
child: Row(
mainAxisSize: MainAxisSize.min,
children: _createLetters(widget.selection),
),
),
);
}
class LetterWidget extends StatelessWidget {
final String playerGuess;
final String codeLetter;
final String selectedLetter;
LetterWidget({this.codeLetter, this.playerGuess, this.selectedLetter});
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 2.0),
child: ConstrainedBox(
constraints: BoxConstraints(minHeight: 50, minWidth: 24),
child: Container(
color: setContainerColor(context),
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Text(
setText(),
style: kGuessText,
textAlign: TextAlign.end,
),
Container(
color: setDividerColor(context),
height: 2,
width: 24,
// indent: 20,
// endIndent: 20,
),
Text(
codeLetter,
style: kCipherText,
),
],
),
),
),
),
);
}
standard size font
accessibility increased font

Flutter/Dart - Dynamic font size to fit Card

I am trying to create some ocassion cards inside a pageview and was wondering if there was a way to make my fontsize dynamic to avoid pixel overflow. Here is a screenshot of a card that works fine:
But when I add an occasion that has more characters...
Here is my code:
class Category {
String name;
IconData icon;
Widget route;
Category(this.name, this.icon, this.route);
}
class CalendarEvents {
String title;
String date;
CalendarEvents(this.title, this.date);
}
class AccountPage extends StatefulWidget {
#override
_AccountPageState createState() => _AccountPageState();
}
class _AccountPageState extends State<AccountPage> {
List<Category> _categories = [
Category('My History', Icons.history, MyHistory()),
Category('Dates to Remember', Icons.event_note, DatesToRemember()),
Category('Terms and Conditions', Icons.assignment, TermsandConditions()),
Category('Privacy Notice', Icons.security, PrivacyNotice()),
Category('Rate us' , Icons.stars, RateUs()),
Category('Send us Feedback' , Icons.feedback, GiveUsFeedback())
];
DateFormat formatter = DateFormat('dd/MM/yyyy');
List<CalendarEvents> _events = [
CalendarEvents('Christmas Day', "25/12/2020"),
CalendarEvents('New Years Eve', "31/12/2020"),
CalendarEvents('New Years Day',"01/01/2021"),
CalendarEvents('Valentines Day', "14/02/2021"),
CalendarEvents('A very long occasion that needs to be resized','01/01/2021')
];
int _index = 0;
#override
Widget build(BuildContext context) {
return SingleChildScrollView(
scrollDirection: Axis.vertical,
child: Column(
children: [
Container(
child: SizedBox(
height: 75, // card height
child: PageView.builder(
itemCount: _events.length,
controller: PageController(viewportFraction: 0.5),
onPageChanged: (int index) => setState(() => _index = index),
itemBuilder: (_, i) {
return Transform.scale(
scale: i == _index ? 1 : 0.5,
child: Card(
elevation: 6,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(formatter.parse(_events[i].date).day.toString()),
Text(DateFormat.MMMM().format(formatter.parse(_events[i].date)).toString()),
Text(
_events[i].title,
style: AppBarTextStyle,
),
],
),
),
);
},
),
),
),
// SizedBox(height: 100.0,),
Container(
height: MediaQuery.of(context).size.height * 0.7,
child: ListView.builder(
itemCount: _categories.length,
itemBuilder: (context, index) {
return Column(
children: <Widget>[
ListTile(
leading: Icon(_categories[index].icon, color: Colors.black,),
title: Text(_categories[index].name),
trailing: Icon(Icons.arrow_forward_ios),
onTap: () => Navigator.push(context, MaterialPageRoute(builder: (context) => _categories[index].route)),
),
Divider(), //
],
);
}),
),
],
),
);
}
}
It would be useful to resize the font depending on the length of occasion. If this is possible, what would be the best way of achieving these results? Thanks
If you want control over the fontsize even after resized, you should use https://pub.dev/packages/auto_size_text package like #Abhishek mentioned
If you just want the text to resize freely, you can use FittedBox and wrap it around Text widget, like this:
FittedBox(
fit: BoxFit.contain,
child: Text()
)
you can use https://pub.dev/packages/auto_size_text package like
title: Text(_categories[index].name),
instead of this
title: AutoSizeText(
_categories[index].name,
minFontSize: 10,
stepGranularity: 10,
maxLines: 4,
overflow: TextOverflow.ellipsis,
)

How to get the height of ListView.builder() widgets to be the height of the entire stack?

I have a ListView.builder() inside a column, and I would like to tell the LisView.builder to set the height according to the height of the widget that it will be returning from the builder method. How do I do that?
Wrap your ListView in a SizedBox with some fixed height. Setting to children height is impractical since Flutter would have to check the size of every element in the list and calculate the maximum.
I am not sure but looking at your question it looks like you want to render listview within the sizes which child widgets are taking
For that we can assign mainAxisSize: MainAxisSize.min, to column and shrinkWrap: true to Listview.builder
class _MyHomePageState extends State<MyHomePage> {
List<String> itemsList = [
"item0",
"item1",
"item2",
"item3",
"item4",
"item5"
];
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("ListView Sample"),
),
body: Container(
color: Colors.black54,
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
ListView.builder(
shrinkWrap: true,
itemCount: itemsList.length,
itemBuilder: (context, index) {
return Container(
child:
Align(
alignment: Alignment.centerRight,
child:Text(
itemsList[index],
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),),
width: 100,
height: 20,
margin: EdgeInsets.only(bottom: 10),
color: Colors.red);
}),
],
),
),
);
}
}
You can use global key to get height of list view or column,
But the height can be fetched only after a build is completed
//user gloabl key to get height of list view or column
GlobalKey key = GlobalKey();
SingleChildScrollView(
child: Column(
key: key,
children: [...]
)
//here you can get widget height
double totalWidgetHeight = key.currentContext!.size!.height;

How to use a ListView.builder to output a widget when a form is submitted

Hi I have a text form field and when the form is filled I want to output the values on multiple pages with the listview.builder currently I can output the data’s in a single container and when I’d fill the form again it doesn’t add another container it just updates the current container’s values
If anyone knows how to do it please help
A complete example:
Source:
import 'package:flutter/material.dart';
class Demo extends StatefulWidget {
#override
_DemoState createState() => new _DemoState();
}
class _DemoState extends State<Demo> {
List<String> _items = new List(); // to store comments
final myController = TextEditingController();
void _addComment() {
if (myController.text.isNotEmpty) {
// check if the comments text input is not empty
setState(() {
_items.add(myController.text); // add new commnet to the existing list
});
myController.clear(); // clear the text from the input
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("DEMO")),
body: Container(
padding: EdgeInsets.all(15),
child: Column(children: [
Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Expanded(
child: TextField(
style: TextStyle(color: Colors.black),
controller: myController,
maxLines: 5,
keyboardType: TextInputType.multiline,
)),
SizedBox(width: 15),
InkWell(
onTap: () {
_addComment();
},
child: Icon(Icons.send))
]),
SizedBox(height: 10),
Expanded(
child: ListView.builder(
itemCount: _items.length,
itemBuilder: (BuildContext ctxt, int index) {
return Padding(
padding: EdgeInsets.all(10),
child: Text("${(index + 1)}. " + _items[index]));
}))
])));
}
}

How to add the widgets dynamically to column in Flutter?

I have added a few widgets inside the ListView. So that I can scroll all widgets. Now I required to add one more widget to the ListView to load the list of comments. I can not add the ListView inside the ListView. And moreover, I do not require the separate scroll for my comments. It should be scroll along with the widgets inside the ListView. So I planned to add the Column instead of ListView. Could any help to add my comments dynamically in the Columns?
new Expanded(
child:
new ListView(
shrinkWrap: true,
children: <Widget>[
// Title
new Padding(padding: const EdgeInsets.only(
top: 10.00, left: 10.00),
child: new Text(
_feed.title, textAlign: TextAlign.start,),
),
// content
new Container(
child: new Text(
_feed.content, textAlign: TextAlign.start,),
),
// Comments List will go here
],
),
),
If you have the comments data already, simply create a List, then pass it to the children property of the Column. Something like:
var commentWidgets = List<Widget>();
for (var comment in comments) {
commentWidgets.Add(Text(comment.text)); // TODO: Whatever layout you need for each widget.
}
…
new Expanded(
child:
new ListView(
shrinkWrap: true,
children: <Widget>[
// Title
new Padding(padding: const EdgeInsets.only(
top: 10.00, left: 10.00),
child: new Text(
_feed.title, textAlign: TextAlign.start,),
),
// content
new Container(
child: new Text(
_feed.content, textAlign: TextAlign.start,),
),
// Comments List will go here
Column(children: commentWidgets,),
],
),
),
If you don't have the comments data already and need to fetch it, use a FutureBuilder to build the UI once the future completes.
Another way:
return Column(
children: [
for(String item in list) Text(item);
]);
You can also mix static and dynamic fields eaisly in this case:
return Column(
children: [
for(String item in list) Text(item),
Text("Static text control"),
]);
By maintaining a reference to the Column object, the .children field/property can be referenced after the Column object has been declared - like so:
Column someColumn = Column(
children: [],
);
someColumn.children.add(Text('Hello 123'));
Currently(2021-06), Flutter 2.x implemented null-safe.
The answer from #Gene Bo should work but needs little modification.
This should work.
var pwdWidgets = <Widget>[];
Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: this.pwdWidgets,
),
ElevatedButton(
onPressed: (){
setState((){
this.pwdWidgets.add(Text("Hello World"),);
});
},
child: Text("click me"),
),
In Addition to #Derek Lakin's Answer which worked for me too, it is also required to call setState(), if you need to update the comments and reload.
var commentWidgets = List<Widget>();
for (var comment in comments) {
commentWidgets.Add(Text(comment.text)); // TODO: Whatever layout you need foreach widget.
}
setState(() {
});
import 'package:flutter/material.dart';
class LoginRegisterPage extends StatefulWidget {
#override
_LoginRegisterPageState createState() => _LoginRegisterPageState();
}
class _LoginRegisterPageState extends State<LoginRegisterPage> {
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: AppBar(
title: Text("App"),
),
body: new Container(
margin: EdgeInsets.all(15.0),
child: new Form(
child: new Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: createInputs() + createButtons(),
),
),
),
);
}
List<Widget> createInputs(){
return{
SizedBox(height: 10.0),
logo(),
SizedBox(height: 20.0),
new TextFormField(
decoration: new InputDecoration(labelText: 'Email'),
),
SizedBox(height: 10.0),
new TextFormField(
decoration: new InputDecoration(labelText: 'Passwors')
),
SizedBox(height: 20.0),
};
}
Widget logo(){
return new Hero(
tag:'hero',
child: new CircleAvatar(
backgroundColor:Colors.transparent,
radius: 110.0,
child: Image.asset("images\logo.PNG"),
),
);
}
List<Widget> createButtons(){
return{
new RaisedButton(
child: new Text("Login", style: new TextStyle(fontSize: 20.0),),
textColor: Colors.pink,
onPressed: () {
},
),
new FlatButton(
child: new Text("Already not have an account?", style: new TextStyle(fontSize: 14.0),),
textColor: Colors.white,
onPressed: () {
},
)
};
}
}
Yes you can add dynamic child on column widgets.
class CategoriesItemWidget extends StatelessWidget {
final List<String> list;
const CategoriesItemWidget({Key? key, required this.list})
: super(key: key);
#override
Widget build(BuildContext context) {
return Column(
children: list
.map((string) => Text(string))
.toList(),
);
}
}