Related
I've been trying to make a content group (image + text) Dismissible as a single unit (swipe-to-delete/remove), but seems as though I can't assign a Widget list as the child parameter to a Dismissible() object. Looking for possible work-arounds or a solution to the problem.
CODE:
class _PhotosListState extends State<PhotosList> {
#override
Widget build(BuildContext context) {
return _buildBody();
}
Widget _buildBody() {
return SizedBox(
height: 485,
child: ListView.builder(
//scrollDirection: Axis.vertical,
//shrinkWrap: true,
itemCount: Photos.length,
itemBuilder: (context,i){
return Dismissible(
background: Container(
color: Colors.green,
),
key: ValueKey<Object>(Photos.items[i]),
onDismissed: (DismissDirection direction) {
setState(() {
Photos.items.removeAt(i);
});
},
child: SizedBox(
child: <Widget> [
Image.asset(Photos.items[i].image),
Text(Photos.items[i].task,
textAlign: TextAlign.center,
style: const TextStyle(color: Colors.grey, fontWeight: FontWeight.bold, fontSize: 19.5)
),
]
)
);
}
)
);
}
}
RESOURCES:
https://medium.com/#mustafatahirhussein/a-guide-to-using-dismissible-widgets-in-flutter-c36c5797d209.
https://api.flutter.dev/flutter/widgets/Dismissible-class.html
In your code of SizedBox:
SizedBox(
child: <Widget> [
Image.asset(Photos.items[i].image),
Text(Photos.items[i].task,
textAlign: TextAlign.center,
style: const TextStyle(color: Colors.grey, fontWeight: FontWeight.bold, fontSize: 19.5)
),
you are passing multiple widgets as a child, but child only takes one argument (i.e, one widget).
So, if you want multiple widgets, you should use a Column widget instead - which can accept a List:
SizedBox(
child: Column(children: <Widget>[
Image.asset(Photos.items[i].image),
Text(Photos.items[i].task,
textAlign: TextAlign.center,
style: const TextStyle(
color: Colors.grey,
fontWeight: FontWeight.bold,
fontSize: 19.5)),
]))
Hence the names - child vs children - it's good to keep this in mind as there can be widgets the accept multiple widgets, i.e. children or a widget that can only accept one child.
As shown in the picture below, there is a container widget with image and text field, and I would like to create an additional container when I click the button.
I have no idea how to implement this.
Can you suggest a way to implement this?
Container _productForm() {
return Container(
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
_productImage(),
_productImage(),
],
),
_heightPadding(15),
_inputText("input text", _controller2),
_heightPadding(15),
_inputText("input text", _controller3),
],
),
);
}
Widget _inputText(String hint, TextEditingController textEditingController) {
return Container(
width: double.infinity,
// height: 200,
child: TextField(
controller: textEditingController,
//엔터키 이벤트
onSubmitted: (value) {},
//높이를 부모 위젯의 높이로 설정 (컨테이너 전체를 텍스트필드로 사용)
// expands: true,
maxLines: null,
decoration: InputDecoration(
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(5),
),
hintText: hint,
),
),
);
}
Widget _addButton() {
return Container(
width: 200,
height: 65,
child: ElevatedButton(
onPressed: () {
//상품썸네일 아래에 상품썸네일 하나 더 추가
},
child: Text(
'Add Container',
style: TextStyle(
fontSize: 17, fontWeight: FontWeight.bold),
),
style: ElevatedButton.styleFrom(
primary: Colors.blue, // background
onPrimary: Colors.white, // foregro// und
),
),
);
}
try put in the List:
List<Container> _containers = [_productForm(),_productForm(),];
onPressed: () {
//상품썸네일 아래에 상품썸네일 하나 더 추가
_containers.add(_productForm());
setState((){});
},
then Load your _containers list with ListView
create list of widget
List<Container> _containers = [_productForm(),_productForm(),];
set dynamic children in row
Row(
children: _containers
.map((_) => _productForm())
.toList(),
)
on press of your button
onPressed: () {
_containers.add(_productForm());
setState((){});
},
There is a simple structure with a Bottom Modal Sheet that contains an input TextField to add a Text into a List and a button to submit the value.
When the text field is focused, it automatically opens the soft keyboard that covers the submit button.
When I dismiss the keyboard in order to press the button, the value in the text field is still visible but the sent value is null.
If I press the button without dismissing the keyboard, the value is correctly sent.
The question is: how can I dismiss the keyboard and still be able to send the typed value after pressing the submit button?
Here is the code:
1) On the main screen, the floating action button shows the modal bottom sheet.
return Scaffold(
floatingActionButton: FloatingActionButton(
onPressed: () {
showModalBottomSheet(
context: context,
builder: (context) => AddTaskScreen(),
},
2) On the AddTaskScreen class, there is a Column with the content of the modal bottom sheet inside a container.
Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Text(
'Add your next Task',
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.lightBlueAccent,
fontSize: 20,
fontWeight: FontWeight.w400,
),
),
TextField(
textAlign: TextAlign.center,
autofocus: true,
onChanged: (value) {
newTaskTitle = value;
},
),
FlatButton(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(
Radius.circular(10),
),
),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
'ADD',
style: TextStyle(
color: Colors.white,
fontSize: 25,
),
),
),
color: Colors.lightBlueAccent,
onPressed: () {
print(newTaskTitle);
},
),
],
),
),
In this simplified version, the value from the TextField is printed in the console when pressing the button. If I press the button without hiding the keyboard it works fine; If I hide the keyboard it passes a null value.
Thanks in advance for your help.
I had the same issue, I resolved it by simply converting called class to extend StatefullWidget instead of StatelessWidget.
In your case converting class AddTaskScreen() to extend StatefullWidget.
Okay, the easiest way to do this is by supplying a TextEditingController to the child class.
So for your case, you can first create a TextEditingController in the Parent Class, then pass it to the child class. And in the TextField inside child class set the controller: The controller you have passed
Parent Class.....
//// other codes ////
TextEditingController textEditingController = TextEditingController();
return Scafold{
FloatingActionButton(
onPressed: () {
showModalBottomSheet(
context: context,
builder: (context) => AddTaskScreen(textEditingController),
},
};
And in the child class
class ChildClass extends StatelessWidget(
final TextEditingController textEditingController;
ChildClass({this.textEditingController});
///then inside build method///
Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Text(
'Add your next Task',
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.lightBlueAccent,
fontSize: 20,
fontWeight: FontWeight.w400,
),
),
TextField(
textAlign: TextAlign.center,
autofocus: true,
controller: textEditingController, /// here add the controller
onChanged: (value) {
newTaskTitle = value;
},
),
FlatButton(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(
Radius.circular(10),
),
),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
'ADD',
style: TextStyle(
color: Colors.white,
fontSize: 25,
),
),
),
color: Colors.lightBlueAccent,
onPressed: () {
print(newTaskTitle);
},
),
],
),
),
Now you can access what was written in the TextField by simply calling textEditingController.value.text from anywhere between these two classes.
Just moving the declarations of text controllers work for me.
....
class _AddPlaceScreenState extends State<AddPlaceScreen> {
final controllerTittlePlace = TextEditingController();
final controllerDescriptionPlace = TextEditingController();
final controllerLocationPlace = TextEditingController();
....
I am having a little problem figuring something out in an app I'm making, any of you who might be able to help your help will be highly appreciated so much. I have tried several solutions but still no luck.
I have two Items inside my body and they both have an IconButton and the problem is that when I click the IconButton it changes on both Item. I want to make it change only on one button and not all. Here is the part of the code for you
Padding(
padding: const EdgeInsets.all(8.0),
child: MyItems("Cheese burger" , Colors.red, "50")
),
Padding(
padding: const EdgeInsets.all(8.0),
child: MyItems("Marshal Burger" ,Colors.grey, "23")
),
And this is how I created MyItems
IconData icon = Icons.favorite_border;
Material MyItems(String foodName, Color color, String price) {
return Material(
color: color, elevation: 0.0,
shadowColor: Color(0x802196F3),
borderRadius: BorderRadius.circular(15.0),
child: Center(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Padding(
padding: const EdgeInsets.all(8.0),
child: Text("$foodName : R$price",
style: TextStyle(color: Colors.black, fontSize: 15.0,
fontWeight: FontWeight.bold),
),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text("",
style: TextStyle(color: Colors.black, fontSize: 12.0,
fontWeight: FontWeight.bold),
),
new IconButton(
icon: Icon(icon, size: 35.0 ,
),
onPressed: () => _onPressed(foodName,price)),
],
)
],
),
),
),
);
}
And this is the function called from the IconButton on pressed.
void _onPressed(String name, String price) async {
SharedPreferences savedCart = await SharedPreferences.getInstance();
String addedToCart = savedCart.getString('$name');
if (addedToCart == null) {
setState(() {
icon = Icons.favorite;
});
savedCart.setString('$name', "$price");
}
else {
setState(() {
icon = Icons.favorite_border;
});
savedCart.remove('$name');
}
}
I want to be able to mark the Icon without changing the other, I kinda have an idea why it is not working I just can't figure out how to solve it
The problem is simple, you pass same widget IconData icon to both of them, so they will both change when that icon changes, in order to fix it you need to separate them with different IconData, here is an example
IconData icon1 = Icons.favorite_border;
IconData icon2 = Icons.favorite_border;
then you need to do one of two things separate functions _onPressed() to do diffent things on each request, or help it define which IconData should be changed..
I would go with the second option, like this:
assuming our foodName(s) is ['apple', 'banana']..
void _onPressed(String name, String price) async {
SharedPreferences savedCart = await SharedPreferences.getInstance();
String addedToCart = savedCart.getString('$name');
var tempIcon;
if (addedToCart == null) {
if(name == 'apple'){
icon1 = Icons.favorite;
} else {
icon2 = Icons.favorite;
}
savedCart.setString('$name', "$price");
}
else {
if(name == 'apple'){
icon1 = Icons.favorite_border;
} else {
icon2 = Icons.favorite_border;
}
savedCart.remove('$name');
}
setState(() {});
}
and simply into code pass the different IconData
Material MyItems(String foodName, Color color, String price) {
return Material(
color: color, elevation: 0.0,
shadowColor: Color(0x802196F3),
borderRadius: BorderRadius.circular(15.0),
child: Center(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Padding(
padding: const EdgeInsets.all(8.0),
child: Text("apple : 50",
style: TextStyle(color: Colors.black, fontSize: 15.0,
fontWeight: FontWeight.bold),
),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text("",
style: TextStyle(color: Colors.black, fontSize: 12.0,
fontWeight: FontWeight.bold),
),
new IconButton(
icon: Icon(icon1, size: 35.0 ,
),
onPressed: () => _onPressed('apple',50)),
],
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Text("banana : 25",
style: TextStyle(color: Colors.black, fontSize: 15.0,
fontWeight: FontWeight.bold),
),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text("",
style: TextStyle(color: Colors.black, fontSize: 12.0,
fontWeight: FontWeight.bold),
),
new IconButton(
icon: Icon(icon2, size: 35.0 ,
),
onPressed: () => _onPressed('banana',25)),
],
)
],
),
),
),
);
}
I have a card that I tap to toggle DND on or off. On the card, at the moment, I have a string of text that says either "DND ON" or "DND OFF". What I am trying to achieve is to add, below "DND ON", in smaller fontsize and in italics: "Alarms, Media, Touch Sounds".
My bare bones code:
Card(
color: Color.fromARGB(255, 76, 175, 80),
elevation: 5.0,
child: InkWell(
splashColor: Colors.blueGrey,
onTap: () {
setState(() {
pressed = !pressed;
});
pressed ? _dndOn() : _dndOff();
},
child: Center(
child: Text(
pressed ? ('DND ON') : ('DND OFF'),
style: TextStyle(
color: Colors.white,
fontSize: 25.0,
),
),
),
),
),
What I have tried: I have tried defining my string in RichText, using TextTheme, and creating a column of text with various styles. Every time I get an error such as "type 'Column' is not a subtype of type 'String'" or "type 'RichText' is not a subtype of type 'String' etc.
Would you have any ideas as to how I could achieve what I want?
You can't use a column widget as the text property of the Text widget. You need to use it in a way where you define the list of children to hold, in this case, the list of Text widgets.
Card(
color: Color.fromARGB(255, 76, 175, 80),
elevation: 5.0,
child: InkWell(
splashColor: Colors.blueGrey,
onTap: () {
setState(() {
pressed = !pressed;
});
pressed ? _dndOn() : _dndOff();
},
child: Center(
child: Column(
children: [
Text(
pressed ? 'DND ON' : 'DND OFF',
style: TextStyle(
color: Colors.white,
fontSize: 25.0,
),
),
Text(
'Alarms, Media, Touch Sounds',
style: TextStyle(
color: Colors.white,
fontSize: 16.0,
),
),
],
),
),
),
),