I have the following Flutter BottomNavigationBar, I have 3 children page widgets. As soon as the user writes a review and press the submit button, I am calling the void _clear() which resets the values in the textfield widgets. This method is inside and called from the WriteReview widget but is not working, the screen is not refreshing. The data it self is being reset but the UI has not be refreshed. I tried with and without setState(){ _clear()}; but no results.
I have similar issue with the TabBarView. What I would expect as a result would be the selected widget page to be refreshed.
I assume is something different on how the widgets are being handled in the TabBarView and BottomNavigationBar that I am missing.
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class HomeView extends StatefulWidget {
#override
_HomeViewState createState() => _HomeViewState();
}
class _HomeViewState extends State<HomeView> {
final String _writeReviewLabel = 'Write review';
final String _searchLabel = 'Search';
final String _accountLabel = 'Account';
var _currentIndex = 0;
final List<Widget> _bottomBarItems = [
WriteReviewView(),
SearchView(),
UserAccountView()
];
#override
Widget build(BuildContext context) {
final accentColor = Theme.of(context).accentColor;
final primaryColor = Theme.of(context).primaryColor;
return BaseView<HomePresenter>(
createPresenter: () => HomePresenter(),
onInitialize: (presenter) {
_currentIndex = ModalRoute.of(context).settings.arguments;
},
builder: (context, child, presenter) => Scaffold(
bottomNavigationBar: BottomNavigationBar(
backgroundColor: primaryColor,
selectedItemColor: accentColor,
unselectedItemColor: Colors.white,
elevation: 0,
currentIndex: _currentIndex,
onTap: (index) {
onTabTapped(index, presenter);
},
items: [
BottomNavigationBarItem(
activeIcon: Icon(Icons.rate_review),
icon: Icon(Icons.rate_review),
title: Text(_writeReviewLabel),
),
BottomNavigationBarItem(
activeIcon: Icon(Icons.search),
icon: Icon(Icons.search),
title: Text(_searchLabel),
),
BottomNavigationBarItem(
activeIcon: Icon(Icons.person),
icon: Icon(Icons.person),
title: Text(_accountLabel)),
],
),
body: _bottomBarItems[_currentIndex]),
);
}
void onTabTapped(int index, HomePresenter presenter) {
if (index == _currentIndex) {
return;
}
if (presenter.isAuthenticated) {
setState(() {
_currentIndex = index;
});
} else {
presenter.pushNamed(context, Routes.login, arguments: () {
if (presenter.isAuthenticated) {
Future.delayed(const Duration(microseconds: 500), () {
setState(() {
_currentIndex = index;
});
});
}
});
}
}
}
Write Review View
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'base/BaseView.dart';
class WriteReviewView extends StatefulWidget {
#override
_WriteReviewViewState createState() => _WriteReviewViewState();
}
class _WriteReviewViewState extends State<WriteReviewView> {
final String _locationLabel = 'Location';
final String _reviewLabel = 'Review';
#override
Widget build(BuildContext context) {
return BaseView<WriteReviewPresenter>(
createPresenter: () => WriteReviewPresenter(),
onInitialize: (presenter) {
presenter.init();
},
builder: (context, child, presenter) => Container(
color: Theme.of(context).backgroundColor,
child: _body(presenter),
));
}
Widget _body(WriteReviewPresenter presenter) {
final height = MediaQuery.of(context).size.height;
return LayoutBuilder(builder: (context, constraint) {
return SingleChildScrollView(
child: ConstrainedBox(
constraints: BoxConstraints(minHeight: constraint.maxHeight),
child: IntrinsicHeight(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Card(
margin: EdgeInsets.fromLTRB(4.0, 4.0, 4.0, 8.0),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Padding(
padding: EdgeInsets.only(top: 10, bottom: 20),
child: Row(
children: [
Icon(Icons.location_on,
color: Theme.of(context).cursorColor),
SectionTitle(_locationLabel),
],
),
),
ChooseAddressButton(presenter.addressContainer.address, () {
presenter.navigateNewAddress(context);
}),
SizedBoxes.medium,
],
),
),
),
Card(
margin: EdgeInsets.fromLTRB(4.0, 4.0, 4.0, 8.0),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: Column(
children: [
Padding(
padding: EdgeInsets.only(top: 10),
child: Row(
children: [
Icon(Icons.star, color: Theme.of(context).indicatorColor),
SectionTitle(_reviewLabel),
],
),
),
SizedBoxes.medium,
CustomTextFormField(
data: presenter.reviewContainer.review,
showMinimum: true,
characterLimit: 50,
maxLines: 4,
height: 100),
ModifyRatingBar(50.0, presenter.reviewContainer.rating, false),
Padding(
padding: EdgeInsets.symmetric(vertical: 5),
child: Divider(indent: 40, endIndent: 40)),
],
),
),
),
Padding(
padding: EdgeInsets.symmetric(horizontal: 10),
child: Card(
color: Theme.of(context).accentColor,
child: FlatButton(
onPressed: () {
_submit(context, presenter);
},
child: Text('Submit',style:TextStyle(fontWeight: FontWeight.bold,fontSize: 18,color: Colors.white)),
),
),
),
],
),
),
),
);
});
}
void _submit(BuildContext context, WriteReviewPresenter presenter) async {
LoadingPopup.show(context);
final status = await presenter.submit(context);
LoadingPopup.hide(context);
if (status == ReviewStatus.successful) {
PostCompletePopup.show(context);
setState(() {
presenter.clear();
});
} else {
presenter.showError(context, status);
}
}
}
setState() only refreshes the widget that called it, and all the widgets under it.
When calling setState()from WriteReview widget, it will not update the HomeView widget. Try moving the setState()call to the HomeView widget using some sort of callback.
Related
I am trying to select one item from phone contacts list (List view widget)
class PhoneContacts extends StatefulWidget {
const PhoneContacts({Key? key}) : super(key: key);
#override
State<PhoneContacts> createState() => _PhoneContactsState();
}
class _PhoneContactsState extends State<PhoneContacts> {
List<Contact> _contacts = [];
late PermissionStatus _permissionStatus;
late Customer _customer;
#override
void initState(){
super.initState();
getAllContacts();
}
void getAllContacts() async {
_permissionStatus = await Permission.contacts.request();
if(_permissionStatus.isGranted) {
List<Contact> contacts = await ContactsService.getContacts(withThumbnails: false);
setState(() {
_contacts = contacts;
});
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Phone Contacts"),
backgroundColor: Colors.indigo[600],
),
body: Container(
padding: const EdgeInsets.all(5),
child: ListView.builder(
itemCount: _contacts.length,
itemBuilder: (BuildContext context, int index) {
Contact contact = _contacts[index];
return contactItem(contact);
}
),
),
);
}
Widget contactItem(Contact contact){
return ListTile(
onTap: () {
Navigator.of(context).push(MaterialPageRoute(builder: (context)=>Dashboard(contact)));
},
leading: const CircleAvatar(
backgroundColor: Colors.pinkAccent,
child: Icon(Icons.person_outline_outlined)),
title : Text(contact.displayName.toString()),
subtitle: Text(contact.phones!.first.value.toString()),
);
}
}
and insert and display it to dashboard list (another List view widget)
class Dashboard extends StatefulWidget {
final Contact? contact;
const Dashboard([this.contact]);
#override
State<Dashboard> createState() => _DashboardState();
}
class _DashboardState extends State<Dashboard> {
final Color? themeColor = Colors.indigo[600];
late GlobalKey<RefreshIndicatorState> refreshKey;
late List<CardGenerator> existingCustomerContactList = getCustomerContactList();
#override
void initState(){
super.initState();
refreshKey=GlobalKey<RefreshIndicatorState>();
}
void addCustomerContact() {
existingCustomerContactList.add(
CardGenerator(
Text(widget.contact!.displayName.toString()),
const Icon(Icons.account_circle),
Text(widget.contact!.phones!.first.value.toString())));
}
List<CardGenerator> getCustomerContactList () {
existingCustomerContactList = [
CardGenerator(
const Text('Dave', style: TextStyle(fontSize: 24.0), textAlign: TextAlign.start,),
const Icon(Icons.account_circle, size: 100, color: Colors.white,),
const Text('Address 1')),
CardGenerator(
const Text('John', style: TextStyle(fontSize: 24.0)),
const Icon(Icons.account_circle, size: 100, color: Colors.white),
const Text('Address 2')),
CardGenerator(
const Text('Richard', style: TextStyle(fontSize: 24.0)),
const Icon(Icons.account_circle, size: 100, color: Colors.white),
const Text('Address 3')),
];
return existingCustomerContactList;
}
Future<void> refreshList() async {
await Future.delayed(const Duration(seconds: 1));
setState(() => {
addCustomerContact(),
getCustomerContactList()
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.grey[50],
appBar: AppBar(
title: const Text("Dashboard"),
backgroundColor: themeColor,
),
body: RefreshIndicator(
key: refreshKey,
onRefresh: () async {
await refreshList();
},
child: Column(
children: [
Expanded(
child: ListView.builder(
itemCount: existingCustomerContactList.length,
key: UniqueKey(),
itemBuilder: (BuildContext context, int index) {
return OpenContainer(
closedColor: Colors.transparent,
closedElevation: 0.0,
openColor: Colors.transparent,
openElevation: 0.0,
transitionType: ContainerTransitionType.fadeThrough,
closedBuilder: (BuildContext _, VoidCallback openContainer) {
return Card(
color: Colors.white,
child: GestureDetector(
onTap: openContainer,
child: SizedBox(
height: 140,
child: Row(
children: [
Container(
decoration: const BoxDecoration(
color: Colors.indigo,
borderRadius: BorderRadius.only(topLeft: Radius.circular(7.0),bottomLeft: Radius.circular(7.0))
),
height: 140,
width: 120,
child: existingCustomerContactList[index].icon,
),
Column(
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: existingCustomerContactList[index].title,
),
Padding(
padding: const EdgeInsets.all(8.0),
child: existingCustomerContactList[index].address,
),
],
)
],
),
),
),
);
},
openBuilder: (BuildContext _, VoidCallback openContainer) {
return ConsumerHome();
}
);
}),
),
],
),
),
);
}
}
I found the
selected item has been added to the Dashboard items list but when I refresh it it doesn't newly added item in the dashboard list view.
I am a newcomer in flutter please bare with me. I already did my search for this problem unfortunately, no luck.
Change the order of execution. You are adding the item in the list and then making a new list again in the current order
addCustomerContact(),
getCustomerContactList()
change this to
getCustomerContactList()
addCustomerContact(),
I'm trying to implement a bottomNavigationBar, but I can't finish it, I'm using Get to handle the routes and the state of the application.
I'm new to flutter, but reading the documentation I still don't understand
This is the main widget.
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
appBar: AppBar(
backgroundColor: AppColors.black,
title: Center(
child: CommonAssetImage(
asset: 'logo.png',
color: AppColors.white,
height: 30,
),
),
),
body: BodyTabsScreen(),
bottomNavigationBar: HomeScreenBottomNavigatorBar()),
);
}
then,I have this widget where call other widget.In this widget I using Obs.
class HomeScreenBottomNavigatorBar extends StatelessWidget {
const HomeScreenBottomNavigatorBar({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Material(
elevation: 10,
color: AppColors.white,
child: Container(
height: 60,
padding: const EdgeInsets.symmetric(horizontal: 27),
color: AppColors.white,
child: Obx(() {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
TabsScreenBottomNavigationTab(
isActive: true,
label: 'Buy',
icon: Icons.home,
onTap: () {}),
TabsScreenBottomNavigationTab(
label: 'My account',
// icon: IkramIcons.user,
// iconSize: 20,
icon: (Icons.home),
onTap: () {}),
],
);
}),
),
);
}
}
class TabsScreenBottomNavigationTab extends StatelessWidget {
final String label;
final IconData icon;
final Widget image;
final VoidCallback onTap;
final bool isActive;
final double iconSize;
const TabsScreenBottomNavigationTab({
Key key,
this.label,
this.icon,
this.image,
this.onTap,
this.isActive,
this.iconSize = 20,
}) : super(key: key);
#override
Widget build(BuildContext context) {
final _inactiveTextStyle = Theme.of(context).textTheme.bodyText2;
final _activeTextStyle =
_inactiveTextStyle.copyWith(color: AppColors.white);
const _commonDuration = Duration(milliseconds: 200);
final _availableSpace = MediaQuery.of(context).size.width - 27 * 2;
final _inactiveWidth = _availableSpace * .2;
final _activeWidth = _availableSpace * .35;
return AnimatedContainer(
duration: _commonDuration,
width: isActive ? _activeWidth : _inactiveWidth,
height: 35,
child: Material(
color: Colors.transparent,
shape: const StadiumBorder(),
clipBehavior: Clip.antiAlias,
child: AnimatedContainer(
duration: _commonDuration,
child: Material(
color: Colors.transparent,
child: InkWell(
onTap: onTap,
child: AnimatedDefaultTextStyle(
style: isActive ? _activeTextStyle : _inactiveTextStyle,
duration: _commonDuration,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
if (icon != null)
Icon(
icon,
size: iconSize,
color: isActive ? AppColors.white : AppColors.black,
),
if (image != null) image,
if (isActive)
Container(
margin: const EdgeInsets.only(left: 8),
child: Text(label),
)
],
),
),
),
),
),
),
);
}
}
Getx will always throw that error when you use Obx or Getx widget without inserting an observable variable that widget. So if you are NOT trying to rebuild a widget based on an updated value of a variable that lives inside a class that exends GetxController, then don't use a Getx widget.
If you're just trying to use Getx for routing, then make sure to change your MaterialApp to GetMaterialApp and define your routes, like so.
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return GetMaterialApp(
home: Page1(),
getPages: [
GetPage(name: Page1.id, page: () => Page1()), // add: static const id = 'your_page_name'; on each page to avoid using raw strings for routing
GetPage(name: Page2.id, page: () => Page2()),
],
);
}
}
Then in the onTap of your bottom navigation bar just use
Get.to(Page2());
Just remove the Obx widget wrapping your Row widget like this:
class HomeScreenBottomNavigatorBar extends StatelessWidget {
const HomeScreenBottomNavigatorBar({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Material(
elevation: 10,
color: AppColors.white,
child: Container(
height: 60,
padding: const EdgeInsets.symmetric(horizontal: 27),
color: AppColors.white,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
TabsScreenBottomNavigationTab(
isActive: true,
label: 'Buy',
icon: Icons.home,
onTap: () {}),
TabsScreenBottomNavigationTab(
label: 'My account',
// icon: IkramIcons.user,
// iconSize: 20,
icon: (Icons.home),
onTap: () {}),
],
);
),
);
}
}
Why? Because you are not using any observable (obs/Rx) variable in your widget tree which would trigger a rebuild when the value changes. So GetX is complaining and for good reason.
The controller should be inside Obx other wise its shows this error.
LeaderBoardController controller = Get.put(getIt<LeaderBoardController>());
Obx(()=>controller.leadBoardModel != null
? Column(
children: [
Container(
height: 180,
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
LeadBoardImage(type: LEADTYPE.NORMAL),
LeadBoardImage(type: LEADTYPE.CROWN),
LeadBoardImage(type: LEADTYPE.NORMAL)
]),
),
Expanded(
flex: 4,
child: ListView(
padding: EdgeInsets.symmetric(horizontal: 10.w),
children: [
for (int i = 4; i < controller.leadBoardModel!.data.result.length; i++)
LeaderBoardListItem(result:controller.leadBoardModel!.data.result[i])
],
),
)
],
)
: LoadingContainer()),
It happens when you don't use your controller value in your widget. That's why it gives error because there is no sense in using Obx or Getx() widget
MainController controller = Get.find();
return Obx(
()
{
return Column(
children: [
Text("My pretty text")
],
);
}
);
Solution :
MainController controller = Get.find();
Obx(
()
{
return Column(
children: [
Text(controller.text)
],
);
}
);
Please note that there are two required aspects: 1) extending from a GetXController, and 2) The field/method returning a value from the controller, must be computed from a Rx type. In my case, I made a sub-class of a GetXController for a test, and the return value was hard-coded (not based on a Rx value), and the ObX error occurred.
For Current Scenario You dont need to use getx for this page
(Its not proper Implementation) . please remove the OBX() your error will gone .
class HomeScreenBottomNavigatorBar extends StatelessWidget {
const HomeScreenBottomNavigatorBar({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Material(
elevation: 10,
color: AppColors.white,
child: Container(
height: 60,
padding: const EdgeInsets.symmetric(horizontal: 27),
color: AppColors.white,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
TabsScreenBottomNavigationTab(
isActive: true,
label: 'Buy',
icon: Icons.home,
onTap: () {}),
TabsScreenBottomNavigationTab(
label: 'My account',
// icon: IkramIcons.user,
// iconSize: 20,
icon: (Icons.home),
onTap: () {}),
],
);
}),
),
}
I'm creating a flutter web project that looks like a dashboard. It has a side navigation and a body area that displays different screens.
To achieve this, I have divided my screen into two parts using Expanded and given them a flex value.
And to display different screens I have used IndexedStack.
Here is my main.dart file :
import 'package:flutter/material.dart';
import 'package:xxx/screens/courses/courses_screen.dart';
import 'package:xxx/side_navigation/menu_item.dart';
import 'package:xxx/componnents/header.dart';
import 'package:websafe_svg/websafe_svg.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Demo',
theme: ThemeData(
visualDensity: VisualDensity.adaptivePlatformDensity,
scaffoldBackgroundColor: Color(0xfffafafa),
fontFamily: 'Poppins',
),
home: MainScreenLayout(),
);
}
}
class MainScreenLayout extends StatefulWidget {
#override
_MainScreenLayoutState createState() => _MainScreenLayoutState();
}
class _MainScreenLayoutState extends State<MainScreenLayout> {
int selectedIndex = 0;
int hoverIndex = -1;
Color bgColor = Colors.transparent;
final List<MenuItem> menus = <MenuItem>[
MenuItem(label: 'Home', icon: Icons.home, screen: Container()),
MenuItem(label: 'Courses', icon: Icons.add, screen: Container()),
MenuItem(label: 'Students', icon: Icons.face_outlined, screen: Container()),
MenuItem(label: 'Home', icon: Icons.home, screen: Container()),
MenuItem(label: 'Courses', icon: Icons.add, screen: Container()),
MenuItem(label: 'Students', icon: Icons.face_outlined, screen: Container()),
];
final List<Widget> _screens = [
Container(
child: Image.asset(
'assets/try.png',
width: 100,
height: 100,
),
),
CoursesScreen(),
Container(
child: WebsafeSvg.asset('assets/folder_icon.svg'),
),
];
void _changeBg(int index) {
setState(() {
hoverIndex = index;
});
}
void _resetBg() {
setState(() {
hoverIndex = -1;
});
}
#override
Widget build(BuildContext context) {
double _width = MediaQuery.of(context).size.width;
return Scaffold(
body: Row(
children: [
Expanded(
flex: 2,
child: Container(
decoration: BoxDecoration(
color: Color(0xffffffff),
borderRadius: BorderRadius.only(
topRight: Radius.circular(_width * 0.03),
bottomRight: Radius.circular(_width * 0.03),
),
),
child: Column(
children: <Widget>[
SizedBox(
height: 50,
),
Flexible(
child: ListView.builder(
itemCount: menus.length,
itemBuilder: (BuildContext context, int index) {
return MouseRegion(
onHover: (event) {
_changeBg(index);
},
onExit: (event) {
_resetBg();
},
child: MenuItemLayout(
bgColor: hoverIndex == index
? Color(0xfffafafa)
: Colors.transparent,
menuItem: menus[index],
isSelected: selectedIndex == index ? true : false,
onTap: () {
setState(() {
selectedIndex = index;
});
},
),
);
},
),
),
],
),
),
),
Expanded(
flex: 7,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Header(
label: menus[selectedIndex].label,
),
Expanded(
child: Container(
child: IndexedStack(
index: selectedIndex,
children: _screens,
),
),
),
],
),
)
],
),
);
}
}
Here is the code for one of the screens (CourseScreen)
import 'package:flutter/material.dart';
import 'package:vjti_dashboard/componnents/responsive.dart';
import 'package:vjti_dashboard/screens/courses/courses_card.dart';
import 'package:vjti_dashboard/screens/courses/courses_model.dart';
class CoursesScreen extends StatelessWidget {
final List<CoursesModel> allCourses = [
CoursesModel(courseId: '123', courseName: 'Ecommerce'),
CoursesModel(courseId: '123', courseName: 'Big Data Analytics'),
CoursesModel(courseId: '123', courseName: 'User Experience Design'),
CoursesModel(courseId: '123', courseName: 'Technical Seminar'),
CoursesModel(courseId: '123', courseName: 'Elective 1'),
];
#override
Widget build(BuildContext context) {
return ListView(
scrollDirection: Axis.vertical,
shrinkWrap: true,
children: [
Sample(
headingLabel: 'First Year',
allCourses: allCourses,
courseColor: Color(0xffEEF1E6),
),
Sample(
headingLabel: 'Second Year',
allCourses: allCourses,
courseColor: Color(0xffF9F1D6),
),
Sample(
headingLabel: 'Third Year',
allCourses: allCourses,
courseColor: Color(0xffE2F0CB),
),
],
);
}
}
class Sample extends StatelessWidget {
final String headingLabel;
final Color courseColor;
final List<CoursesModel> allCourses;
const Sample({Key key, this.allCourses, this.headingLabel, this.courseColor})
: super(key: key);
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 30, vertical: 20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
GestureDetector(
onTap: () {},
child: Container(
padding: EdgeInsets.all(16),
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
color: Color(0xffd6d6d6),
width: 1.0,
),
),
),
child: Text(headingLabel),
),
),
GridView.count(
primary: false,
physics: ScrollPhysics(), // to disable GridView's scrolling
shrinkWrap: true,
padding: const EdgeInsets.symmetric(vertical: 24, horizontal: 0),
crossAxisSpacing: 20,
childAspectRatio: 3 / 1,
mainAxisSpacing: 20,
crossAxisCount: Responsive.isDesktop(context)
? 4
: Responsive.isTablet(context)
? 3
: 2,
children: List.generate(
allCourses.length,
(index) {
return CourseCard(
coursesModel: allCourses[index],
containerColor: courseColor,
);
},
),
),
],
),
);
}
}
The side navigation works as it should and different pages are loaded while the navigation sticks to the left.
When I click on any widget on the CourseScreen, I want to open another screen that would replace CourseScreen but the navigation should still be there.
How can I achieve this?
Note : I'm new to flutter and most of the code that I have written is not perfect and probably is not a good way. I would appreciate if you can point out bad codes in the above files.
Thank You!!!
onGenerateRoute: (settings) => MaterialPageRoute(
builder: (context) => Parent(),
I have ListTile in the ListView with RaisedButton as trailing, I want to change color and icon on btn clicked, trouble is if I change it on setstate method all listTile buttons change. So how to determine each one?
Widget _getList(BuildContext context,int index,) {
return Card(
elevation: 3,
child: Column(
children: <Widget>[
ListTile(
leading: Image.asset(
"assets/" + _allDevices[index].image,
fit: BoxFit.cover,
),
title: Text(_allDevices[index].name),
subtitle: Text(_allDevices[index].desc),
trailing: SizedBox.fromSize(
size: Size(56, 56), // button width and height
child: ClipOval(
child: RaisedButton(
elevation: 2,
splashColor: Colors.red,
color: Colors.blue,
onPressed: () {
setState(() {
//pro should do something here... switch index or something....
});
},
child: Icon(Icons.lock_open),
),
)),
onTap: () {},
)
],
),
);
}
Find this sample, All needed is bool flag in the model class which maintains the click status. On click set it true, if it's already true then set it as false.
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class Devices {
String name = '';
bool isSelected = false;
Devices(this.name, this.isSelected);
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: MyWidget(),
);
}
}
class MyWidget extends StatefulWidget {
#override
MyWidgetState createState() => MyWidgetState();
}
class MyWidgetState extends State<MyWidget> {
var _allDevices = [
Devices('Text', false),
Devices('Text', false),
Devices('Text', false),
Devices('Text', false)
];
Widget _getList(BuildContext context, int index) {
return Card(
elevation: 3,
child: Column(
children: <Widget>[
ListTile(
leading: Text('Text'),
title: Text(_allDevices[index].name),
subtitle: Text(_allDevices[index].name),
trailing: SizedBox.fromSize(
size: Size(56, 56), // button width and height
child: ClipOval(
child: RaisedButton(
elevation: 2,
color: _allDevices[index].isSelected
? Colors.green
: Colors.blue,
onPressed: () {
setState(() {
if (_allDevices[index].isSelected) {
_allDevices[index].isSelected = false;
} else{
_allDevices[index].isSelected = true;
}
});
},
child: Icon(Icons.lock_open),
),
)),
onTap: () {},
)
],
),
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
ListView.builder(
shrinkWrap: true,
itemCount: 4,
itemBuilder: (context, index) {
return _getList(context, index);
})
]));
}
}
I am using the package
country_code_picker: ^1.4.0
https://pub.dev/packages/country_code_picker#-installing-tab-
with flutter 1.17.3
Which is pretty much one of the only country code picker packages. But I have one serious problem an I don't have a clue what it could be.
When I run this code
import 'package:flutter/material.dart';
import 'package:country_code_picker/country_code_picker.dart';
void main() {
runApp(App());
}
class App extends StatelessWidget {
App();
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: TestWidget(),
);
}
}
class TestWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(body: _buildCountryPicker(context));
}
Widget _buildCountryPicker(BuildContext context) {
return Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
child: Center(
child: CountryCodePicker(
initialSelection: 'NL',
),
),
);
}
}
And I open the dialog to select a country. I scroll in the list and then select the TextField my keyboard opens and when I try to type something my entire app freezes. I can't even hot reload. I don't get a single error.
I am running this on my Huawei P30, but I also experience this on other android devices. I don't know if this is a flutter bug or a country code picker bug.
I think it is probably in this widget somewhere. If anyone could point me in the right direction it would help me alot!
class SelectionDialog extends StatefulWidget {
final List<CountryCode> elements;
final bool showCountryOnly;
final InputDecoration searchDecoration;
final TextStyle searchStyle;
final TextStyle textStyle;
final WidgetBuilder emptySearchBuilder;
final bool showFlag;
final double flagWidth;
final Size size;
final bool hideSearch;
/// elements passed as favorite
final List<CountryCode> favoriteElements;
SelectionDialog(
this.elements,
this.favoriteElements, {
Key key,
this.showCountryOnly,
this.emptySearchBuilder,
InputDecoration searchDecoration = const InputDecoration(),
this.searchStyle,
this.textStyle,
this.showFlag,
this.flagWidth = 32,
this.size,
this.hideSearch = false,
}) : assert(searchDecoration != null, 'searchDecoration must not be null!'),
this.searchDecoration =
searchDecoration.copyWith(prefixIcon: Icon(Icons.search)),
super(key: key);
#override
State<StatefulWidget> createState() => _SelectionDialogState();
}
class _SelectionDialogState extends State<SelectionDialog> {
/// this is useful for filtering purpose
List<CountryCode> filteredElements;
#override
Widget build(BuildContext context) => SimpleDialog(
titlePadding: const EdgeInsets.all(0),
title: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.end,
children: <Widget>[
IconButton(
padding: const EdgeInsets.all(0),
iconSize: 20,
icon: Icon(
Icons.close,
),
onPressed: () => Navigator.pop(context),
),
if (!widget.hideSearch)
Padding(
padding: const EdgeInsets.symmetric(horizontal: 24),
child: TextField(
style: widget.searchStyle,
decoration: widget.searchDecoration,
onChanged: _filterElements,
),
),
],
),
children: [
Container(
width: widget.size?.width ?? MediaQuery.of(context).size.width,
height:
widget.size?.height ?? MediaQuery.of(context).size.height * 0.7,
child: ListView(
children: [
widget.favoriteElements.isEmpty
? const DecoratedBox(decoration: BoxDecoration())
: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
...widget.favoriteElements.map(
(f) => SimpleDialogOption(
child: _buildOption(f),
onPressed: () {
_selectItem(f);
},
),
),
const Divider(),
],
),
if (filteredElements.isEmpty)
_buildEmptySearchWidget(context)
else
...filteredElements.map(
(e) => SimpleDialogOption(
key: Key(e.toLongString()),
child: _buildOption(e),
onPressed: () {
_selectItem(e);
},
),
),
],
),
),
],
);
Widget _buildOption(CountryCode e) {
return Container(
width: 400,
child: Flex(
direction: Axis.horizontal,
children: <Widget>[
if (widget.showFlag)
Flexible(
child: Padding(
padding: const EdgeInsets.only(right: 16.0),
child: Image.asset(
e.flagUri,
package: 'country_code_picker',
width: widget.flagWidth,
),
),
),
Expanded(
flex: 4,
child: Text(
widget.showCountryOnly
? e.toCountryStringOnly()
: e.toLongString(),
overflow: TextOverflow.fade,
style: widget.textStyle,
),
),
],
),
);
}
Widget _buildEmptySearchWidget(BuildContext context) {
if (widget.emptySearchBuilder != null) {
return widget.emptySearchBuilder(context);
}
return Center(
child: Text('No country found'),
);
}
#override
void initState() {
filteredElements = widget.elements;
super.initState();
}
void _filterElements(String s) {
s = s.toUpperCase();
setState(() {
filteredElements = widget.elements
.where((e) =>
e.code.contains(s) ||
e.dialCode.contains(s) ||
e.name.toUpperCase().contains(s))
.toList();
});
}
void _selectItem(CountryCode e) {
Navigator.pop(context, e);
}
}
Also filed an issue on the flutter github https://github.com/flutter/flutter/issues/59886
Edit:
I have a video of it right here
https://www.youtube.com/watch?v=669KitFG9ek&feature=youtu.be
I just had to remove the keys, so there probably was a duplicate key
...filteredElements.map(
(e) => SimpleDialogOption(
//key: Key(e.toLongString()),
child: _buildOption(e),
onPressed: () {
_selectItem(e);
},
),
),