I have a dummy list of data. I want each item on the list to show a different widget when it is tapped, similar to a contacts app. Defining the widget in the onPressed method always returns the same widget. How can I generate each widget without manually creating each one?
void az() {
int c = "A".codeUnitAt(0);
int end = "Z".codeUnitAt(0);
while (c <= end) {
items.add(FlatButton.icon(
icon: Icon(Icons.image_aspect_ratio),
label: Text(
String.fromCharCode(c),
style: TextStyle(fontSize: 20),
),
onPressed: (){
print(String.fromCharCode(c)); //This should return a different widget
},
));
c++;
}
}
You can copy paste run full code below
You can declare a local variable start in for loop
code snippet
void az() {
int c = "A".codeUnitAt(0);
int end = "Z".codeUnitAt(0);
for (int start = c; start <= end; start++) {
items.add(FlatButton.icon(
icon: Icon(Icons.image_aspect_ratio),
label: Text(
String.fromCharCode(start),
style: TextStyle(fontSize: 20),
),
onPressed: () {
print(String.fromCharCode(
start)); //This should return a different widget
},
));
}
}
output
I/flutter (12880): A
I/flutter (12880): B
I/flutter (12880): C
working demo
full code
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
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;
List<Widget> items = [];
void az() {
int c = "A".codeUnitAt(0);
int end = "Z".codeUnitAt(0);
for (int start = c; start <= end; start++) {
items.add(FlatButton.icon(
icon: Icon(Icons.image_aspect_ratio),
label: Text(
String.fromCharCode(start),
style: TextStyle(fontSize: 20),
),
onPressed: () {
print(String.fromCharCode(
start)); //This should return a different widget
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
SecondRoute(yourParameter: String.fromCharCode(start))));
},
));
}
}
void _incrementCounter() {
setState(() {
_counter++;
});
}
#override
void initState() {
// TODO: implement initState
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
az();
setState(() {});
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: items,
),
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
class SecondRoute extends StatelessWidget {
final String yourParameter;
const SecondRoute({Key key, this.yourParameter}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('$yourParameter'),
),
body: Center(
child: RaisedButton(
onPressed: () {
Navigator.pop(context);
},
child: Text('Go back!'),
),
),
);
}
}
Related
I'm trying to increment the app bar icon by making a custom app bar. But I can't update the value with setState. I tried to update the value by the setAppBarValue function, but it didn't work.
MyAppBar.of(context)?.setAppBarValue(_counter);
import 'package:badges/badges.dart';
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: MyAppBar(),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
_incrementCounter();
MyAppBar.of(context)?.setAppBarValue(_counter);
},
tooltip: 'Increment',
child: const Icon(Icons.add),
),
);
}
}
class MyAppBar extends StatefulWidget implements PreferredSizeWidget {
MyAppBar() : super();
#override
Size get preferredSize => const Size.fromHeight(60);
#override
_MyAppBarState createState() => _MyAppBarState();
static _MyAppBarState? of(BuildContext context) =>
context.findAncestorStateOfType<_MyAppBarState>();
}
class _MyAppBarState extends State<MyAppBar>
with SingleTickerProviderStateMixin {
int _appBarValue = 0;
setAppBarValue(int value) {
setState(() {
_appBarValue = value;
});
}
#override
Widget build(BuildContext context) {
return AppBar(
title: const Text("teste"),
actions: <Widget>[Center(child: _alertBadge(context))]);
}
Widget _alertBadge(BuildContext context) {
return Badge(
position: BadgePosition.topEnd(
top: 0,
end: 3,
),
badgeStyle: BadgeStyle(
badgeColor: Colors.red,
),
badgeContent: Text(
_appBarValue.toString(),
style: TextStyle(color: Colors.white),
),
child:
IconButton(icon: Icon(Icons.shopping_bag_outlined), onPressed: () {}),
);
}
}
You Have to pass the increment variable value to the appbar widget like this:
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: MyAppBar(
appBarValue: _counter,
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
_incrementCounter();
// MyAppBar.of(context)?.setAppBarValue(_counter);
},
tooltip: 'Increment',
child: const Icon(Icons.add),
),
);
}
}
And use that value in your app bar widget:
class MyAppBar extends StatefulWidget implements PreferredSizeWidget {
const MyAppBar({
Key? key,
required this.appBarValue,
}) : super(key: key);
final int appBarValue;
#override
Size get preferredSize => const Size.fromHeight(60);
#override
_MyAppBarState createState() => _MyAppBarState();
static _MyAppBarState? of(BuildContext context) =>
context.findAncestorStateOfType<_MyAppBarState>();
}
class _MyAppBarState extends State<MyAppBar>
with SingleTickerProviderStateMixin {
// int _appBarValue = 0;
//
// setAppBarValue(int value) {
// setState(() {
// _appBarValue = value;
// });
// }
#override
Widget build(BuildContext context) {
return AppBar(
title: const Text("teste"),
actions: <Widget>[Center(child: _alertBadge(context))]);
}
Widget _alertBadge(BuildContext context) {
return Badge(
position: BadgePosition.topEnd(
top: 0,
end: 3,
),
badgeStyle: BadgeStyle(
badgeColor: Colors.red,
),
badgeContent: Text(
widget.appBarValue.toString(),
style: TextStyle(color: Colors.white),
),
child:
IconButton(icon: Icon(Icons.shopping_bag_outlined), onPressed: () {}),
);
}
}
CODE:
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return GetMaterialApp(
title: 'Flutter Demo',
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> {
int _counter = 0;
void _incrementCounter() {
_counter++;
}
#override
Widget build(BuildContext context) {
TestInt testInt2 = TestInt(100);
TestInt testInt1 = TestInt(0);
Get.put<TestInt>(testInt2, tag: "testInt2");
Get.put<TestInt>(testInt1, tag: "testInt1");
return Scaffold(
appBar: AppBar(
// Here we take the value from the MyHomePage object that was created by
// the App.build method, and use it to set our appbar title.
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times: $_counter',
),
GetBuilder<TestInt>(
init: Get.find<TestInt>(tag: "testInt2"),
builder: (_) {
return Text(
'${Get.find<TestInt>(tag: "testInt2").number}',
style: Theme.of(context).textTheme.headline4,
);
},
),
GetBuilder<TestInt>(
init: Get.find<TestInt>(tag: "testInt1"),
builder: (_) {
return Text(
'${Get.find<TestInt>(tag: "testInt1").number}',
style: Theme.of(context).textTheme.headline4,
);
},
),
],
),
),
persistentFooterButtons: [
FloatingActionButton(
onPressed: () {
_incrementCounter();
Get.find<TestInt>(tag: "testInt2").increment();
},
tooltip: 'Increment',
child: Icon(Icons.add),
),
FloatingActionButton(
onPressed: () {
_incrementCounter();
Get.find<TestInt>(tag: "testInt1").increment();
},
tooltip: 'Increment',
child: Icon(Icons.add),
),
], // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
class TestInt extends GetxController {
late int number;
TestInt(number) {
this.number = number;
}
void increment() {
this.number = number + 1;
update();
}
}
Summary:
On the code above, I made 2 floating buttons and 2 GetBuilder to show testInt1's number and testInt2's number separately.
But Only one button works well and the other button doesn't work.
I think it might works only first initiation with init: Get.find<TestInt>(tag: "testInt2"), and second init: Get.find<TestInt>(tag: "testInt1"), does not works..
Question:
How to make both works?
Set global:false to your GetBuilder.
GetBuilder<TestInt>(
init: Get.find<TestInt>(tag: "testInt2"),
global: false,
builder: (_) {
return Text(
'${Get.find<TestInt>(tag: "testInt2").number}',
style: Theme.of(context).textTheme.headline4,
);
},
),
In one of my Flutter app pages, I want to disable the next button at first. If the user enters any new value in the textfield, I want to enable next button. Here is my code, but the next button stays disabled no matter what. How can I enable the next button when a user enters a value?
#override
Widget build(BuildContext context) {
bool btnenabled = false;
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.red,
title: Text('Address'),
),
body: Column(
children: <Widget>[
.....
TextFormField(
onChanged: (newValue) {
_currentaddress = '$addressJSON' + newValue;
if (newValue.length > 0) {
setState(() {
btnenabled = true;
});
}
}),
TextButton(
style: TextButton.styleFrom(
backgroundColor: Colors.blue[700], primary: Colors.white),
child: Text('next',
),
onPressed: btnenabled == true
? () => Navigator.push(context,
MaterialPageRoute(builder: (context) => Summary()))
: null)
],
),
);
}
}
You can copy paste run full code below
You can move bool btnenabled = false; out of Widget build
change from
Widget build(BuildContext context) {
bool btnenabled = false;
to
bool btnenabled = false;
...
Widget build(BuildContext context) {
working demo
full code
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#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> {
bool btnenabled = false;
String _currentaddress = "";
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.red,
title: Text('Address'),
),
body: Column(
children: <Widget>[
TextFormField(onChanged: (newValue) {
_currentaddress = 'addressJSON' + newValue;
if (newValue.length > 0) {
setState(() {
btnenabled = true;
});
}
}),
TextButton(
style: TextButton.styleFrom(
backgroundColor: Colors.blue[700], primary: Colors.white),
child: Text(
'next',
),
onPressed: btnenabled == true
? () => Navigator.push(context,
MaterialPageRoute(builder: (context) => Summary()))
: null)
],
),
);
}
}
class Summary extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Text("Summary");
}
}
leading: GestureDetector(
behavior: HitTestBehavior.translucent,
onTap: () {
// There I want to change title, background color etc
},
),
title: Text('title'),
dense: false,
),
I want to change properties of list tile from its own onTap()
You can copy paste run full code below
You can use StatefulWidget and call setState inside onTap
code snippet
class _ListTileCustomState extends State<ListTileCustom> {
String _title = "title";
Color _color = Colors.blue;
#override
Widget build(BuildContext context) {
return ListTile(
tileColor: _color,
leading: GestureDetector(
behavior: HitTestBehavior.translucent,
onTap: () {
setState(() {
_title = "changed";
_color = Colors.red;
});
},
child: Icon(Icons.add)),
title: Text(_title),
dense: false,
);
}
}
working demo
full code
import 'package:flutter/material.dart';
class ListTileCustom extends StatefulWidget {
#override
_ListTileCustomState createState() => _ListTileCustomState();
}
class _ListTileCustomState extends State<ListTileCustom> {
String _title = "title";
Color _color = Colors.blue;
#override
Widget build(BuildContext context) {
return ListTile(
tileColor: _color,
leading: GestureDetector(
behavior: HitTestBehavior.translucent,
onTap: () {
setState(() {
_title = "changed";
_color = Colors.red;
});
},
child: Icon(Icons.add)),
title: Text(_title),
dense: false,
);
}
}
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
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;
void _incrementCounter() {
setState(() {
_counter++;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
ListTileCustom(),
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
you can define one variable before build a method and use that variable to the title of listtile and when you ontap then change the state using setState and It will change the title of your listtie.
leading: GestureDetector(
behavior: HitTestBehavior.translucent,
onTap: () {
setState((){
title="your text"
});
},
),
title: Text(title),
dense: false,
),
here title is variable
and ListTile also has property of onTap
I have list of orders orderList. If that isEmpty, FloatingActionButton is hide. In case orderList have products - FAB will be shown. My code:
bool statusFAB = false;
_getFABState(){
setState(() {
if(!orderList.isEmpty){
statusFAB = true;
}
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: _getFAB(),
backgroundColor: _kAppBackgroundColor,
body: Builder(
builder: _buildBody,
),
);
Widget _getFAB() {
if(statusFAB){
return FloatingActionButton(
backgroundColor: Colors.deepOrange[800],
child: Icon(Icons.add_shopping_cart),
onPressed: null);
}
}
It's not working, because condition work once, but state of orderList can be change anytime.
You don't need to store the statusFAB variable, you can just evaluate it on the fly. See updated sample below:
#override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: _getFAB(),
backgroundColor: _kAppBackgroundColor,
body: Builder(
builder: _buildBody,
),
);
Widget _getFAB() {
if (orderList.isEmpty) {
return Container();
} else {
return FloatingActionButton(
backgroundColor: Colors.deepOrange[800],
child: Icon(Icons.add_shopping_cart),
onPressed: null);
}
}
Well there is a shortcut which can be used with the ternary operator and can be used within Scaffold of a Stateful Widget as
floatingActionButton: orderList.isEmpty ? Container() : FloatingActionButton(...)
Unless you need a long and complicated function, this works fine. Even if you need a complicated function, then that function can be called only when the drawing was needed
floatingActionButton: orderList.isEmpty ? Container() : ComplicatedFn(...)
Widget ComplicatedFn() {
//.... Complicated Algo
return FloatingActionButton(...)
}
import 'package:flutter/material.dart';
void main() => runApp(new 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(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
List<Product> orderList = List();
int counter = 0;
void getCount(){
setState(() {
counter = orderList.length;
});
}
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text(widget.title),
),
body: Center(
child: Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
IconButton(
onPressed: (){
if(orderList.isNotEmpty)
orderList.removeLast();
getCount();
},
icon: Icon(Icons.remove),
color: Colors.red,
),
Text('$counter'),
IconButton(
onPressed: (){
orderList.add(Product('product'));
getCount();
print('product added');
},
icon: Icon(Icons.add),
color: Colors.blue,
)
],
),
),
),
floatingActionButton: _getFAB()
);
}
Widget _getFAB() {
if (orderList.isEmpty) {
return Container();
} else {
return FloatingActionButton(
backgroundColor: Colors.deepOrange[800],
child: Icon(Icons.shopping_cart),
onPressed: null);
} }
}
class Product {
String title;
Product(this.title);
}