Reputation: 197
In the following code i can add and remove Tabs to the screen. For removing, i have defide a Button on the AppBar that after pressing it a DropdownMenu appears who let me select which Tab i want to remove and it removes the selected Item. The problem that i have is that when i select a item DropdownMenu it does not show the selected item.
Thanks in advance for some help.
Follows the complete code:
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
debugShowCheckedModeBanner: false,
home: Home(),
);
}
}
class Home extends StatefulWidget {
const Home({super.key});
@override
HomeState createState() => HomeState();
}
class HomeState extends State<Home> {
String? selectedTab = tabs[0].text;
var tabName = "";
static List<Tab> tabs = [
const Tab(text: ""),
];
List<Widget> tabViewChildren = [
Container(
height: 400,
),
];
@override
Widget build(BuildContext context) {
return DefaultTabController(
initialIndex: 0,
length: tabs.length,
child: Scaffold(
appBar: AppBar(
actions: <Widget>[
ElevatedButton.icon(
onPressed: () {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: const Text("Enter tab name"),
content: TextField(
onChanged: (String value) {
tabName = value;
},
),
actions: <Widget>[
ElevatedButton(
child: const Text("Add"),
onPressed: () {
setState(() {
tabs.add(Tab(text: tabName));
tabViewChildren.add(Container(height: 400));
});
Navigator.of(context).pop();
},
),
],
);
},
);
},
icon: const Icon(
Icons.add_box,
),
label: const Text('Add Tab'),
),
Opacity(
opacity: tabs.isNotEmpty ? 1 : 0.4,
child: ElevatedButton.icon(
onPressed: () {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: const Text("Select tab to remove"),
content: tabs.isNotEmpty
? DropdownButton<String>(
items: tabs
.map((tab) => DropdownMenuItem<String>(
value: tab.text,
child: Text(tab.text ?? ""),
))
.toList(),
onChanged: (String? value) {
setState(() {
selectedTab = value;
});
},
value: selectedTab,
)
: Container(),
actions: <Widget>[
ElevatedButton(
child: const Text("Remove"),
onPressed: () {
setState(() {
int index = tabs.indexWhere((tab) => tab.text == selectedTab);
tabs.removeAt(index);
tabViewChildren.removeAt(index);
selectedTab = tabs.isNotEmpty ? tabs[0].text : null;
});
Navigator.of(context).pop();
},
),
],
);
},
);
},
icon: const Icon(Icons.remove),
label: const Text('Remove Tab'),
),
),
],
title: const Text("Tab in Flutter"),
bottom: TabBar(tabs: tabs),
),
body: TabBarView(children: tabViewChildren)));
}
}
Upvotes: 0
Views: 49
Reputation: 121
The Problem: Flutter works as a tree, each node has its own build context so showDialog is returning a build with a new build context, therefore in your code whenever you call setState in the dialog => you are calling the setState for the parent context (page), basically, you are updating the Screen widget not the dialog widget.
The Solution: you have to use StatefulBuilder inside the Dialog widget so that it will have its own setState functionality. see the code below
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
debugShowCheckedModeBanner: false,
home: Home(),
);
}
}
class Home extends StatefulWidget {
const Home({super.key});
@override
HomeState createState() => HomeState();
}
class HomeState extends State<Home> {
String? selectedTab = tabs[0].text;
var tabName = "";
static List<Tab> tabs = [
const Tab(text: ""),
];
List<Widget> tabViewChildren = [
Container(
height: 400,
),
];
@override
Widget build(BuildContext context) {
return DefaultTabController(
initialIndex: 0,
length: tabs.length,
child: Scaffold(
appBar: AppBar(
actions: <Widget>[
ElevatedButton.icon(
onPressed: () {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: const Text("Enter tab name"),
content: TextField(
onChanged: (String value) {
tabName = value;
},
),
actions: <Widget>[
ElevatedButton(
child: const Text("Add"),
onPressed: () {
setState(() {
tabs.add(Tab(text: tabName));
tabViewChildren.add(Container(height: 400));
});
Navigator.of(context).pop();
},
),
],
);
},
);
},
icon: const Icon(
Icons.add_box,
),
label: const Text('Add Tab'),
),
Opacity(
opacity: tabs.isNotEmpty ? 1 : 0.4,
child: ElevatedButton.icon(
onPressed: () {
showDialog(
context: context,
builder: (BuildContext context) {
return StatefulBuilder(
builder: (context, setState) => AlertDialog(
title: const Text("Select tab to remove"),
content: tabs.isNotEmpty
? DropdownButton<String>(
items: tabs
.map(
(tab) => DropdownMenuItem<String>(
value: tab.text,
child: Text(tab.text ?? ""),
))
.toList(),
onChanged: (String? value) {
selectedTab = value;
setState(() {});
},
value: selectedTab,
)
: Container(),
actions: <Widget>[
ElevatedButton(
child: const Text("Remove"),
onPressed: () {
setState(() {
int index = tabs.indexWhere(
(tab) => tab.text == selectedTab);
tabs.removeAt(index);
tabViewChildren.removeAt(index);
selectedTab =
tabs.isNotEmpty ? tabs[0].text : null;
});
Navigator.of(context).pop();
},
),
],
),
);
},
);
},
icon: const Icon(Icons.remove),
label: const Text('Remove Tab'),
),
),
],
title: const Text("Tab in Flutter"),
bottom: TabBar(tabs: tabs),
),
body: TabBarView(children: tabViewChildren)));
}
}
Upvotes: 2