When I click on my card widget, I want it to be dropdown according to my upcoming data. I have text and icon on my card. I added Inkwell to be clickable. but as I click, I want my coming data to be added down. Is this possible?
child: Card(
child: InkWell(
onTap: () { });
},
child: Row(
children: [
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 8.0),
child: Icon(
Icons.favorite,
),
),
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 8.0),
child: Text(
snapshot.text,
style: TextStyle(fontSize: 16),
),
),
],
),
),
),
I have a list like this,
and when an item is clicked, I want related subtext as below. For example, I want 2 companies related to the country to come. Like the card open. (Sorry for the bad visuals. i did it quickly:))
It is indeed possible, the whole idea is based on this theory:
Your country card and it's associated company card should be a different StatefulWidget, which will be controlled by the boolean
Please note: I have given a very basic representation of the view, you can play with the UI and make it look like your own desired UI
This is your card view should look like:
class CardWidget extends StatefulWidget {
// right now it only accepts title, but you can add more
// arguments to be accepted by this widget
CardWidget({Key key, this.title}) : super(key: key);
final String title;
#override
_CardWidgetState createState() => _CardWidgetState();
}
class _CardWidgetState extends State<CardWidget> {
// responsible for toggle
bool _showData = false;
#override
Widget build(BuildContext context) {
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 10.0),
// list card containing country name
GestureDetector(
onTap: (){
setState(() => _showData = !_showData);
},
child: Container(
width: MediaQuery.of(context).size.width,
decoration: BoxDecoration(
color: Colors.white,
boxShadow: [BoxShadow(color: Colors.grey, offset: Offset(0.0, 3.0))]
),
child: Padding(
padding: EdgeInsets.all(15.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
// add your other icon here
Text(widget.title)
]
)
)
)
),
// this is the company card which is toggling based upon the bool
_showData ? Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: ['Kia','Samsung'].map((e){
// make changes in the UI here for your company card
return Card(child: Text(e));
}).toList()
) : SizedBox() // else blank
]
);
}
}
Here how I am calling the CardWidget in the main UI
// this is dummy data, you can play with the data and the UI
Column(
children: ['Korea', 'China', 'Japan', 'USA', 'India'].map((country){
// returning the CardWidget passing only title
return CardWidget(title: country);
}).toList()
)
Result
Related
I have a list.generate and a floating action button,
here my list showing all transactions , but facing a problem that when I scroll down to last transaction, I can't see right side of transaction as floating action button coming on transaction card
so I want a more some space only while I reach at bottom of list..
here is my simple code
class ShowTransactionWidget extends StatelessWidget {
final List<Transaction> mylist;
ShowTransactionWidget({required this.mylist});
//Todo add little bit more space while scrolling
#override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Padding(
padding: EdgeInsets.only(left: 20, right: 20),
child: Column(
children: List.generate(mylist.length, (index) {
return Column(
children: [
TransactionCard(transaction: mylist[index],),
const Padding(
padding: const EdgeInsets.only(left: 20.0, top: 8.0),
child: Divider(
thickness: 0.9,
),
)
],
);
}),
),
),
],
);
}
}
I have attached an image regarding it
You can add another widget on Column based on last index like
children: List.generate(mylist.length, (index) {
return Column(
children: [
.....
if (index == mylist.length - 1)
SizedBox(
height: 20,
),
],
);
}),
Based on your UI. I will prefer using ListView.separated and ListView provides padding.
I have implement several no of expanded items using expansion widget.
These are the steps
All the widgets are collapsed at the beginning
First widget was expanded
Second widget also expanded without collapse first one
I want to automatically collapse first one when expanding second one
import 'package:expansion_widget/expansion_widget.dart';
import 'package:flutter/material.dart';
class CustomExpansionTile extends StatefulWidget {
final Widget HeaderBody;
final Widget ExpandedBody;
final Color HeaderColor;
final Color ExpandedBodyColor;
final double Padding;
const CustomExpansionTile({
Key? key,
required this.HeaderBody,
required this.ExpandedBody,
required this.HeaderColor,
required this.ExpandedBodyColor,
required this.Padding,
}) : super(key: key);
#override
_CustomExpansionTileState createState() => _CustomExpansionTileState();
}
class _CustomExpansionTileState extends State<CustomExpansionTile> {
#override
Widget build(BuildContext context) {
return Column(
children: [
Card(
elevation: 0,
child: ExpansionWidget(
initiallyExpanded: false,
titleBuilder:
(double animationValue, _, bool isExpaned, toogleFunction) {
return Container(
decoration: BoxDecoration(
color: widget.HeaderColor,
borderRadius: BorderRadius.circular(6)),
height: 59,
child: InkWell(
onTap: () {
toogleFunction(animated: true);
},
child: Padding(
padding: EdgeInsets.symmetric(horizontal: widget.Padding),
child: Row(
children: [
widget.HeaderBody,
const Spacer(),
Transform.rotate(
angle: 0,
child: Icon(
isExpaned
? Icons.keyboard_arrow_down_rounded
: Icons.keyboard_arrow_right,
size: 40),
alignment: Alignment.centerRight,
)
],
),
)),
);
},
content: Container(
color: widget.ExpandedBodyColor,
width: double.infinity,
padding: const EdgeInsets.all(20),
child: Column(
children: [widget.ExpandedBody],
),
),
),
),
],
);
}
}
This is my Code for calling custom widget
CustomExpansionTile(
HeaderBody: Row(
children: [
Text('Hellooo'),
Text('Hellooo'),
],
),
ExpandedBody: Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Text('Hellooo'),
Text('Hellooo'),
],
),
HeaderColor: Colors.white,
ExpandedBodyColor: Colors.white,
Padding: 0,
),
To achieve that you can do one of the following:
1- [O(n): O(1)] create a new property called currentSelectedItem its type is as your data model type and onTap method change its value to your tapped item and do not forget to add the check to the expansion property of the expanded card as -> expanded: currentSelectedItem == itemModel,
2- [O(n): O(n)] add a new boolean property to your model item called "isExpanded" and config on tap action to loop throw all models list changing items' isExpanded to false and update current tapped item's isExpanded to true
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
I have the following situation
Column(
children: [
Tabs(),
getPage(),
],
),
the getPage method
Widget getPage() {
if (tab1IsSelected == true) {
return Container(
child: Center(
child: Text('Tab1'),
),
);
}
if (tab1IsSelected == false) {
return Container(
child: Center(
child: Text('Tab2'),
),
);
}
}
and globally I have declared a variable
bool tab1IsSelected = true;
In the Tabs Class (statefull):
class Tabs extends StatefulWidget {
#override
_TabsState createState() => _TabsState();
}
class _TabsState extends State<Tabs> {
#override
Widget build(BuildContext context) {
return Row(
children: [
Expanded(
child: GestureDetector(
onTap: () {
setState(() {
tab1IsSelected = true;
});
},
child: Container(
decoration: BoxDecoration(
color: tab1IsSelected ? primary : second,
),
child: Padding(
padding:
EdgeInsets.only(top: 1.5 * SizeConfig.heightMultiplier),
child: Center(
child: Text(
'New Hunt',
style: Theme.of(context).textTheme.bodyText1,
),
),
),
),
),
),
Expanded(
child: GestureDetector(
onTap: () {
setState(() {
tab1IsSelected = false;
});
},
child: Container(
decoration: BoxDecoration(
color: tab1IsSelected ? second : primary,
),
child: Padding(
padding:
EdgeInsets.only(top: 1.5 * SizeConfig.heightMultiplier),
child: Center(
child: Text(
'My Hunts',
style: Theme.of(context).textTheme.bodyText2,
),
),
),
),
),
),
],
);
}
}
I change the value of that bool, but only if I hot reload the page the content is changing. Why?
Can you guide me please?
I've tried to use ? : in that Column but the same result and if I declare that variable in the Main Class where the Column is, I can't access it in the Tabs class, so that's why I declared it globally, maybe that's the cause I have to hot reload, but how can I implement that to do what I want. Thank you in advance
setState is inside _TabsState so it will only affect/rebuilt that particular widget, not getPage(), you could try using ValueChanged<bool> to retrieve the new value and then using setState in the widget that wraps the getPage()
class Tabs extends StatefulWidget {
final ValueChanged<bool> onChanged;
Tabs({this.onChanged});
#override
_TabsState createState() => _TabsState();
}
class _TabsState extends State<Tabs> {
#override
Widget build(BuildContext context) {
return Row(
children: [
Expanded(
child: GestureDetector(
onTap: () => widget.onChanged(true), //pass the value to the onChanged
child: Container(
decoration: BoxDecoration(
color: tab1IsSelected ? primary : second,
),
child: Padding(
padding:
EdgeInsets.only(top: 1.5 * SizeConfig.heightMultiplier),
child: Center(
child: Text(
'New Hunt',
style: Theme.of(context).textTheme.bodyText1,
),
),
),
),
),
),
Expanded(
child: GestureDetector(
onTap: () => widget.onChanged(false), //pass the value to the onChanged
child: Container(
decoration: BoxDecoration(
color: tab1IsSelected ? second : primary,
),
child: Padding(
padding:
EdgeInsets.only(top: 1.5 * SizeConfig.heightMultiplier),
child: Center(
child: Text(
'My Hunts',
style: Theme.of(context).textTheme.bodyText2,
),
),
),
),
),
),
],
);
}
}
Now on the widget with the column (That should be a StatefulWidget for setState to work)
Column(
children: [
Tabs(
onChanged: (bool value) => setState(() => tab1IsSelected = value);
),
getPage(),
],
),
everytime you change the value of tab1IsSelected it will update getPage()
If you want to rebuild a widget when something in its state changes you need to call the setState() of the widget.
The variable is referenced to the State class and when you call setState() Flutter will rebuild the widget itself by calling the build() method of the State class.
If you want to have some variables outside the widgets I suggest you to use a state management approach listed here: https://flutter.dev/docs/development/data-and-backend/state-mgmt/options.
For example you could use Provider to store the active tab and reference the provider variable in both widgets.
You can try to handle the setstate in the parent class holding the Tab Widget then pass a the function to tab class and execute it in the gesture detector.
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(),
);
}
}