This is a single page application.
I have created a drawer inside the Scaffold of HomePage. There are three list tile items in the list view below the title.
When I click the list tile I was able to change the title of the drawer through the onTap method.
But how can I replace the present list tile items with new list tile items after clicking any of the present list tile items.
In general how can I update the list tile items after pressing on any of the items in drawer
Before clicking an item
After clicking change font, the list is unchanged
What I want after clicking change font
I tried making a new function that returns a list view and called it in the setState inside onTap and also calling outside setState but inside the onTap
I did a simple sample here, you can check it out if it satisfies your need.Simple Drawer
you can run it on dart pad as well
Dart pad
import 'package:flutter/material.dart';
const Color darkBlue = Color.fromARGB(255, 18, 32, 47);
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark().copyWith(
scaffoldBackgroundColor: darkBlue,
),
debugShowCheckedModeBanner: false,
home: Scaffold(
appBar: AppBar(),
drawer: const Drawer(child: MyDrawer()),
body: Center(
child: MyWidget(),
),
),
);
}
}
enum MenuItem {
font("Change Font",
['Commic Sans', 'Product Sans', 'Monserrat', 'ubuntu', 'unbounded']),
theme("Change Theme", ['dark', 'light']),
metrics("Change Metrics", ['small', 'medium', 'large']);
final String label;
final List<String> items;
const MenuItem(this.label, this.items);
}
class MyDrawer extends StatefulWidget {
const MyDrawer({super.key});
#override
State<MyDrawer> createState() => _MyDrawerState();
}
class _MyDrawerState extends State<MyDrawer> {
MenuItem? selectedMenu;
#override
Widget build(BuildContext context) {
return SafeArea(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 20),
child:
Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
const SizedBox(height: 20),
Text(
'Settings',
style: Theme.of(context).textTheme.headlineMedium,
),
const SizedBox(height: 60),
Container(height: 1.0, color: Colors.grey),
const SizedBox(height: 60),
// if a menu item is selected, display its sub items
if (selectedMenu != null)
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
BackButton(onPressed: (){
setState(() {
selectedMenu = null;
});
},),
for (var item in selectedMenu!.items)
InkWell(
onTap: () {
print(item);
},
child: Padding(
padding: const EdgeInsets.all(10),
child: Text(
item,
style: Theme.of(context).textTheme.bodySmall,
)))
])
else
for (var item in MenuItem.values)
InkWell(
onTap: () {
setState(() {
selectedMenu = item;
});
},
child: Padding(
padding: const EdgeInsets.all(10),
child: Text(
item.label,
style: Theme.of(context).textTheme.bodySmall,
)))
])));
}
}
class MyWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Text(
'Hello, World!',
style: Theme.of(context).textTheme.headlineSmall,
);
}
}
Related
I want to create a generic Layout which accepts a child Widget as a parameter, that lays out the content as follows:
I have an AppBar at the Top, a Title (headline), and below that the Content (could be anything). At the bottom, I have a Column with a few buttons. If the content is too big for the screen, all those widgets, except the AppBar, are scrollable. If the content fits the screen, the title and content should be aligned at the top, and the buttons at the bottom.
To showcase what I mean, I created a drawing:
It is easy to create to scrollable content functionality. But I struggle with laying out the content so that the buttons are aligned at the bottom, if the content does NOT need to be scrollable.
It is important to say that I don't know the height of the content widget or the buttons. They are dynamic and can change their height. Also, the title is optional and can have two different sizes.
What I tried is the following:
import 'package:flutter/material.dart';
class BaseScreen extends StatelessWidget {
final String? title;
final bool bigHeader;
final Widget child;
final Widget bottomButtons;
const BaseScreen({
Key? key,
required this.child,
required this.bottomButtons,
this.bigHeader = true,
this.title,
}) : super(key: key);
#override
Widget build(BuildContext context) {
final AppBar appBar = AppBar(
title: Text("AppBar"),
);
double minChildHeight = MediaQuery.of(context).size.height -
MediaQuery.of(context).viewInsets.bottom -
MediaQuery.of(context).viewInsets.top -
MediaQuery.of(context).viewPadding.bottom -
MediaQuery.of(context).viewPadding.top -
appBar.preferredSize.height;
if (title != null) {
minChildHeight -= 20;
if (bigHeader) {
minChildHeight -= bigHeaderStyle.fontSize!;
} else {
minChildHeight -= smallHeaderStyle.fontSize!;
}
}
final Widget content = Column(
mainAxisSize: MainAxisSize.min,
children: [
if (title != null)
Text(
title!,
style: bigHeader ? bigHeaderStyle : smallHeaderStyle,
textAlign: TextAlign.center,
),
if (title != null)
const SizedBox(
height: 20,
),
ConstrainedBox(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
child,
bottomButtons,
],
),
constraints: BoxConstraints(
minHeight: minChildHeight,
),
),
],
);
return Scaffold(
appBar: appBar,
body: SingleChildScrollView(
child: content,
),
);
}
TextStyle get bigHeaderStyle {
return TextStyle(fontSize: 20);
}
TextStyle get smallHeaderStyle {
return TextStyle(fontSize: 16);
}
}
The scrolling effects work perfectly, but the Buttons are not aligned at the bottom. Instead, they are aligned directly below the content. Does anyone know how I can fix this?
DartPad you can check here
customscrollview tutorial
Scaffold(
// bottomNavigationBar: ,
appBar: AppBar(
title: Text(" App Bar title ${widgets.length}"),
),
//============
body: CustomScrollView(
slivers: [
SliverFillRemaining(
hasScrollBody: false,
child: Column(
// controller: _mycontroller,
children: [
title,
...contents,
// ---------------------This give Expansion and button get down --------
Expanded(
child: Container(),
),
// ---------------------This give Expansion and button get down --------
Buttons
],
),
)
],
))
We can Achieve with the help of CustomScrollView widget and Expanded widget.here Expanded widget just expand between the widget
Sample Code
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
void main() {
runApp(
MaterialApp(debugShowCheckedModeBanner: false, home: MyApp()),
);
}
class MyApp extends StatefulWidget {
#override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
var widgets = [];
var _mycontroller = ScrollController();
#override
Widget build(BuildContext context) {
var title = Center(
child: Text(
"Scrollable title ${widgets.length}",
style: TextStyle(fontSize: 30),
));
var contents = [
...widgets,
];
var Buttons = Row(
children: [
Expanded(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
height: 100,
child: ElevatedButton(
onPressed: () {
setState(() {
widgets.add(Container(
height: 100,
child: ListTile(
title: Text(widgets.length.toString()),
subtitle: Text("Contents BTN1"),
),
));
});
// _mycontroller.jumpTo(widgets.length * 100);
},
child: Text("BTN1"),
),
),
)),
Expanded(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
height: 100,
child: ElevatedButton(
onPressed: () {
setState(() {
if (widgets.length > 0) {
widgets.removeLast();
}
});
// _mycontroller.jumpTo(widgets.length * 100);
},
child: Text("BTN2"),
),
),
))
],
);
return MaterialApp(
debugShowCheckedModeBanner: false,
home: SafeArea(
child: Scaffold(
// bottomNavigationBar: ,
appBar: AppBar(
title: Text(" App Bar title ${widgets.length}"),
),
body: CustomScrollView(
slivers: [
SliverFillRemaining(
hasScrollBody: false,
child: Column(
// controller: _mycontroller,
children: [
title,
...contents,
Expanded(
child: Container(),
),
Buttons
],
),
)
],
)),
),
);
}
}
Try this:
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp();
#override
Widget build(BuildContext context) {
return MaterialApp(
home: BaseScreen(
bottomButtons: [
ElevatedButton(onPressed: () {}, child: const Text('Button 1')),
ElevatedButton(onPressed: () {}, child: const Text('Button 2')),
],
content: Container(
color: Colors.lightGreen,
height: 200,
),
title: 'Title',
),
);
}
}
class BaseScreen extends StatelessWidget {
final bool bigHeader;
final List<Widget> bottomButtons;
final String? title;
final Widget content;
const BaseScreen({
this.bigHeader = true,
required this.bottomButtons,
required this.content,
this.title,
});
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('AppBar'),
),
body: CustomScrollView(
slivers: [
SliverFillRemaining(
hasScrollBody: false,
child: Column(
children: [
if (title != null)
Padding(
padding: const EdgeInsets.symmetric(vertical: 12),
child: Text(
title!,
style: bigHeader ? _bigHeaderStyle : _smallHeaderStyle,
textAlign: TextAlign.center,
),
),
content,
const Spacer(),
...bottomButtons,
],
),
),
],
),
);
}
TextStyle get _bigHeaderStyle => const TextStyle(fontSize: 20);
TextStyle get _smallHeaderStyle => const TextStyle(fontSize: 16);
}
Screenshots:
without_scrolling
scrolled_up
scrolled_down
I've created a button that allows the user to add a credit card, the cards are being added to a Listview.builder.
The problem is that when I have multiple cards and I select one, it selects all of them, it's probably a state problems but I didn't find (yet) how to fix it, here is the dartpad : [dartpad][1] of my code if you can check it and maybe show me what I'm doing wrong, it's probably failing inside the buildBody but I'm not really sure and I have not successfully found a solution yet.
You simply have to tap two times on 'Add a card' and you will see when checking one of them, both will get selected.
For some reason the link isnt working through the shortcut so here it is https://dartpad.dev/b0aaaa2901aa3ac67426d9bdd885abb1:
I modified your dartpad code to get the behaviour you are trying to achive:
The code is provided below:
The issue was that you are using the same bool value _isSelected for the two Checkboxes.
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Center(
child: InformationsBancairesPage(),
),
),
);
}
}
class InformationsBancairesPage extends StatefulWidget {
#override
_InformationsBancairesPageState createState() =>
_InformationsBancairesPageState();
}
class _InformationsBancairesPageState extends State<InformationsBancairesPage> {
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
title: Text(
'Payer ou recevoir un paiement'.toUpperCase(),
style: TextStyle(fontSize: 19, color: Colors.black),
),
centerTitle: true,
backgroundColor: Colors.white,
iconTheme: IconThemeData(color: Colors.black),
),
body: Padding(
padding: const EdgeInsets.all(15.0),
child: ListView(
children: <Widget>[
InputAddCarte(),
],
),
),
);
}
}
class InputAddCarte extends StatefulWidget {
#override
_InputAddCarteState createState() => _InputAddCarteState();
}
class _InputAddCarteState extends State<InputAddCarte> {
// create a list of bool values for your checkboxes
List<bool> _selectedList = [false, false];
int value = 0;
void initState() {
super.initState();
}
_addCard() {
setState(() {
value = value + 1;
print(value);
});
}
Widget buildBody(BuildContext context, int indexClicked) {
return LabeledCheckbox(
label: 'Card credit',
padding: const EdgeInsets.symmetric(horizontal: 20.0),
// pass the value of the checkbox at the selected index
value: _selectedList[indexClicked],
onChanged: (bool newValue) {
setState(() {
// pass the value of the checkbox at the selected index
_selectedList[indexClicked] = newValue;
});
},
);
}
#override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
ButtonTheme(
minWidth: 250,
child: RaisedButton(
color: Color(0xff00cc99),
child: Text(
'ADD A CARD'.toUpperCase(),
style: TextStyle(color: Colors.white, fontSize: 18),
),
onPressed: _addCard,
),
),
// Show the cards when you press 'Ajouter une carte'
ListView.builder(
shrinkWrap: true,
itemCount: this.value,
itemBuilder: (BuildContext context, int value) {
// display two cards maximum
if (value < 2) {
// pass the index of the selected checkbox
return buildBody(context, value);
}
return Container();
},
),
ButtonTheme(
minWidth: 250,
child: RaisedButton(
color: Colors.orange,
child: Text(
'Delete a card'.toUpperCase(),
style: TextStyle(color: Colors.white, fontSize: 18),
),
onPressed: () {},
),
)
],
);
}
}
// Create custom checkbox for the list of cards
class LabeledCheckbox extends StatelessWidget {
const LabeledCheckbox({
this.label,
this.padding,
this.value,
this.onChanged,
});
final String label;
final EdgeInsets padding;
final bool value;
final Function onChanged;
#override
Widget build(BuildContext context) {
return InkWell(
onTap: () {
onChanged(!value);
},
child: Padding(
padding: padding,
child: Row(
children: <Widget>[
Expanded(child: Text(label)),
Checkbox(
value: value,
onChanged: (bool newValue) {
onChanged(newValue);
},
),
],
),
),
);
}
}
I used a couple other threads to create an app where you type in some text in a textfield and when you press the button a default container is added to a list with the text in one of the fields. However when I type the text and add the widget the text is changed for all entries instead of just for the one that was added. This is my code:
import 'dart:core';
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Flutter Demo',
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: new MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int count = 0;
TextEditingController noteSend = TextEditingController();
#override
Widget build(BuildContext context) {
List<Widget> children = new List.generate(
count,
(int i) => new InputWidget(
i,
noteRec: noteSend.text,
));
return new Scaffold(
appBar: new AppBar(title: new Text('some title')),
body: Column(
children: <Widget>[
Container(
child: TextField(
controller: noteSend,
),
color: Colors.lightBlueAccent,
width: 150,
height: 50,
),
Expanded(
child: ListView(
children: children,
scrollDirection: Axis.vertical,
),
),
],
),
floatingActionButton: new FloatingActionButton(
child: new Icon(Icons.add),
onPressed: () {
setState(() {
count = count + 1;
});
},
));
}
}
class InputWidget extends StatefulWidget {
final int index;
final String noteRec;
InputWidget(this.index, {Key key, this.noteRec}) : super(key: key);
#override
_InputWidgetState createState() => _InputWidgetState();
}
class _InputWidgetState extends State<InputWidget> {
#override
Widget build(BuildContext context) {
return new Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(10),
),
child: Row(
children: <Widget>[
Column(
children: <Widget>[
Icon(
Icons.image,
size: 75,
)
],
),
Container(
margin: EdgeInsets.only(left: 80, right: 30),
child: Column(
children: <Widget>[
Text('Note'),
],
),
),
Column(
children: <Widget>[
Text("${widget.noteRec}"),
],
),
],
),
);
}
}
How can I make the Text different with every entry?
List<Widget> children = new List.generate(
count,
(int i) => new InputWidget(
i,
noteRec: noteSend.text,
));
In this code, you set the input text for all the elements in children. It's the reason all the entries are changed to the same text. You can save the text to a list of the string when you press the save button and call it in List.generate:
List<Widget> children = new List.generate(
count,
(int i) => new InputWidget(
i,
noteRec: listString[i],
));
Try this code
Container(
height: 200,
child: Column(
children: <Widget>[
Expanded(
child: buildGridView(),
)
],
),
),
Then call the buildGridView that will return Widgets
Widget buildGridView() {
return Text('text here'); // your future widget
}
The value is effectively changing when clicking but the animation doesn't show :
Here's my code :
var editGender = Padding(
padding: const EdgeInsets.only(top: 12.0),
child: Column(
children: <Widget>[
CheckboxListTile(
value: _male,
onChanged: _maleChanged,
title: Text("Male"),
activeColor: Theme.of(context).primaryColor,
),
CheckboxListTile(
value: _female,
onChanged: _femaleChanged,
title: Text("Female"),
activeColor: Theme.of(context).primaryColor,
)
],
),
);
When tapping the edit button :
FlatButton(
onPressed: (){
buildShowRoundedModalBottomSheet(context, title, editGender, option);
},
child: Text('Edit'),
it shows the bottom sheet :
Future buildShowRoundedModalBottomSheet(BuildContext context, String title, Widget content,[String date]) {
return showRoundedModalBottomSheet(
context: context,
radius: 20.0,
builder: (context){
return Padding(
padding: const EdgeInsets.only(top: 20.0, bottom: 20.0, left: 20.0, right: 20.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text(
title,
style: TextStyle(
fontFamily: 'SamsungSans',
fontSize: 20.0,
),
),
content,
...
I am passing the same context to the widget :/
setState would change the value but it wouldn't rebuild your bottom sheet as it is being called on a onPressed of a FlatButton. You are certainly not invoking that onPressed again but you wouldn't want to do it either.
As I mentioned in the comments a StatefulBuilder would do the job.
A working example
import 'package:flutter/material.dart';
import 'package:rounded_modal/rounded_modal.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: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
bool value = false;
void _incrementCounter() {
showRoundedModalBottomSheet(
context: context,
builder: (context) {
return StatefulBuilder(builder: (context, setState) {
return Container(
height: 200.0,
child: Checkbox(value: value, onChanged: (val) {
setState(() {
value = val;
});
}),
);
});
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.display1,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
As commented by #10101010, you'll have to use a Stateful widget. And In _femaleChanged and _maleChanged, you'll have to use setState(). Example :
void _femaleChanged(bool value) => setState(() => _female = value);
Unable to create dynamic list view with text and radio button in flutter
when i am creating dynamically(Fetching the data from service and binding the data with text ) list view with text and radio button when i am selecting radio button it is not showing selected (indicator );
import 'package:flutter/material.dart';
class DemoTest extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: new DemoTestStatesFull(),
);}}
class DemoTestStatesFull extends StatefulWidget {
DemoTestState createState() => DemoTestState();
}
class DemoTestState extends State {
final List<String> floor_list = [
"floor1",
"floor2",
"floor3",
"floor4",
"floor5",
"floor6",
];
int _selectedIndex =0;
int _value2=0;
Container datacontainer=new Container();
_onSelected(int index) {
setState(() => _selectedIndex = index);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Dynamic View Example'),
),
body: Stack(
children: <Widget>[
new RaisedButton(
child: Text("Fetch Data"),
onPressed:() {setState(() {
datacontainer= DisplayData(floor_list);// Let Suppose Here getting data from web service after json parsing string list is
print("Button is clicked");
});}),
datacontainer,
],));}
Widget DisplayData(List<String>floorlist)
{
datacontainer =new Container(
height: 300.00,
width: 400.00,
child: ListView.builder(
itemCount: (floorlist.length),
itemBuilder: (context, i) {
return new InkResponse(
enableFeedback: true,
onTap: ()
{
print(" Card index is $i");
},
child:new Card(
color: _selectedIndex != null && _selectedIndex == i ? Colors.greenAccent : Colors.white,
elevation: 3.0,
margin: EdgeInsets.fromLTRB(5.00, 0.00, 5.00,5.00),
child: Row(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Padding(padding: const EdgeInsets.fromLTRB(20.00,0.0, 0.0,0.0)),
Padding(
padding: const EdgeInsets.all(10.0),
child: Text(floorlist[i], style: new
TextStyle(fontSize: 20.0,
fontWeight: FontWeight.bold)),
),
Padding(padding: const EdgeInsets.fromLTRB(70.00,
0.0, 0.0,0.0)),
Padding(
padding: const EdgeInsets.all(10.0),
child: Text(floorlist[i], style: new
TextStyle(fontSize: 20.0,
fontWeight: FontWeight.bold)),
),
new Radio(
value: i,
groupValue: _value2,
onChanged:(int value)
{
setState(() {
_value2=value;
print("Radio index is $value");
});}),])));}
),);return datacontainer;}}
just i want to create dynamic list view with text and select able radio button
first you can easily use gestureDetector for the row without the radio button and
put radio button at trailing , listTile is like a row for the list and have title and trailing under it as a relative position inside the tile (listTile is the list row you want that contains text and a radio button) .
ps : GestureDetector is a widget so you can return it instead of the left side of the row.
return ListTile(
leading:GestureDetector(
child: //add the text here,
onTap: () {
//here handle the onclick for text
} ,
) ,
trailing://here add the radio button with onchanged normally,
);
hope that answers your question.