I'm trying to create a custom TabBar which navigates only forward with a button click.
im using an enum to manage the state and couple of class objects to manage items for Tabs.
What result I expected
VS
What result I've got
The code I worked out is as follows. I am still trying to figure out a way of managing the expected outcome with my code implementation. if anyone can help out will be amazing.
Object classes
class CategoryModel {
final String? title;
final eCategory category;
// final eCategoryState? catState;
CategoryModel({this.title, required this.category});
}
class CategoryStateModel {
final CategoryModel? tItem;
late eTabState? tState;
CategoryStateModel({this.tItem, this.tState});
}
The code portion
class _ContributeTabScreenState extends State<ContributeTabScreen>
with SingleTickerProviderStateMixin {
eButtonState _submitBtnState = eButtonState.bActive;
eButtonState _continueBtnState = eButtonState.bActive;
int pageNo = 0;
TabController? _tabController;
late List<eTabState>? _tabState = [];
late List<CategoryModel>? _categoryModelList = [];
late List<Tab>? _tabList = [];
late List<CategoryStateModel>? _catStateList = [];
#override
void initState() {
_categoryModelList = widget.catList;
_assignTabs();
_tabController = new TabController(
vsync: this,
length: _categoryModelList!.length, //3,
);
super.initState();
// print(_categoryModelList![0].title.toString());
}
List<Tab> _assignTabs() {
for (var item = 0; item < _categoryModelList!.length; item++) {
//
if (item != 0) {
for (var t in _categoryModelList!) {
_catStateList!.add(
new CategoryStateModel(tItem: t, tState: eTabState.tUnSelected));
}
} else {
for (var t in _categoryModelList!) {
_catStateList!.add(
new CategoryStateModel(tItem: t, tState: eTabState.tSelected));
}
}
//
_tabList!.add(
new Tab(
child: _tabItem(_catStateList![item]),
// child: _tabItem(_categoryModelList![item]),
),
);
}
return _tabList!;
}
void _goBack() {
Navigator.of(context).pop();
}
//the onPressed call back I manage the forward move of a tabbar item + tabview
void forwardTabPage() {
if (pageNo >= 0 && pageNo < _categoryModelList!.length) {
setState(() {
// });
// setState(() {
pageNo = pageNo + 1;
// _catStateList![pageNo - 1].tState = eTabState.tCompleted;
// _tabState![pageNo - 1] = _catStateList![pageNo - 1].tState!;
});
_tabController!.animateTo(pageNo);
}
}
...rest of the code
//the Tabbar item
_tabItem(CategoryStateModel item) => Container(
width: 140.0,
alignment: Alignment.center,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
item.tItem!.title!,
style: tBody4.copyWith(color: CustomColors.mDarkBlue),
),
item.tState == _catStateList![pageNo].tState //eTabState.tCompleted
? SizedBox(
width: 8.0,
)
: SizedBox(
width: 0.0,
),
item.tState == _catStateList![pageNo].tState //eTabState.tCompleted
? CircleAvatar(
radius: 12.0,
backgroundColor: CustomColors.green600,
child: Icon(
Icons.check_outlined,
size: 20.0,
),
)
: SizedBox(
width: 0.0,
),
],
),
);
continueBtn() => ButtonWidget(
btnColor: CustomColors.green600,
borderColor: CustomColors.green600,
textColor: CustomColors.mWhite,
text: "Continue",
eButtonType: eButtonType.bText,
eButtonState: _continueBtnState,
onPressed: () {
forwardTabPage();
// _tabState = eTabState.tCompleted;
},
);
submitBtn() => ButtonWidget(
btnColor: CustomColors.green600,
borderColor: CustomColors.green600,
textColor: CustomColors.mWhite,
text: "Submit",
eButtonType: eButtonType.bText,
eButtonState: _submitBtnState,
onPressed: () {},
);
#override
Widget build(BuildContext context) {
return DefaultTabController(
length: _categoryModelList!.length,
child: Scaffold(
appBar: AppBar(
toolbarHeight: 60.0,
leadingWidth: 100.0,
leading: GestureDetector(
onTap: _goBack,
child: Container(
child: Row(
children: [
SizedBox(
width: 16.0,
),
const Icon(
Icons.arrow_back_ios,
color: CustomColors.grey700,
size: 24.0,
),
Flexible(
child: Text(
"Back",
style: tButtonSmall,
),
),
],
),
),
),
backgroundColor: Colors.white,
elevation: 0.0,
bottom: PreferredSize(
preferredSize: Size.fromHeight(50.0),
child: IgnorePointer(
child: TabBar(
controller: _tabController,
isScrollable: false,
enableFeedback: false,
padding: EdgeInsets.only(bottom: 8.0),
indicatorSize: TabBarIndicatorSize.label,
indicator:
// _tabState == eTabState.tSelected
// ?
BoxDecoration(
borderRadius: BorderRadius.circular(40.0),
color: CustomColors.green300,
border: Border.all(color: CustomColors.mGreen, width: 2),
),
tabs: _tabList!
),
),
),
),
body: Container(
child: TabBarView(
controller: _tabController,
physics: NeverScrollableScrollPhysics(),
children: [
Container(
color: CustomColors.grey600.withOpacity(0.2),
child: Center(
child: Text("Home Tab View"),
),
),
Center(
child: Text("Map Tab View"),
),
],
),
),
persistentFooterButtons: [
Container(
width: MediaQuery.of(context).size.width,
// height: 40,
padding: EdgeInsets.symmetric(horizontal: 24.0),
margin: EdgeInsets.symmetric(vertical: 16.0),
child: (_categoryModelList!.length == 1 ||
pageNo == _categoryModelList!.length - 1)
? submitBtn()
: continueBtn(),
)
],
),
);
}
enum classes
enum eCategoryState {
cSelected,
cUnSelected,
cEnded,
}
enum eTabState {
tSelected,
tUnSelected,
tCompleted,
}
enum eCategory {
cAccess,
cAmenities,
}
I don't know if I understand what you mean or not. but try this. TabBar has a isScrollable property. set it to true
Related
This bounty has ended. Answers to this question are eligible for a +50 reputation bounty. Bounty grace period ends in 11 hours.
KJEjava48 is looking for a canonical answer.
In my flutter application screen I have a DropdownButtonFormField2 which list various financial years, where after fetching the financial year list I set the current financial year as the initial(default) selected value in the dropdown as code below.The Problem comes when I change the financial year to a different year in dropdown, and it will change to the newly selected financial year in dropdown, but it will reset to the current(initial/default) financial year when I rotate the screen. How to solve this issue?
class AccountSetup extends StatelessWidget {
FinancialYear selFinancialPeriod = FinancialYear();
dynamic selFinancialValue;
List<dynamic>? financialYearList;
final Company selectedCompany;
AccountSetup({Key? key, required this.selectedCompany}) : super(key: key);
#override
Widget build(BuildContext context) {
context.read<MyAccountBloc>().add(FetchFinancialYearList(selectedCompany.id!));
return BlocBuilder<MyAccountBloc, MyAccountState>(
builder: (context, state) {
if(state is FinancialYearList) {
financialYearList = state.list;
if(financialYearList != null) {
for(dynamic itemFY in financialYearList!) {
if(DateTime.now().isBetween(yMdFormat.parse(itemFY['startDate']), yMdFormat.parse(itemFY['endDate']))) {
selFinancialPeriod = FinancialYear.fromJson(itemFY);
selFinancialValue = itemFY;
break;
}
}
}
getFinancialPeriodDropDown(context);
} else if(state is AccountTabChanged) {
....
} else if(state is UpdateDropDown) {
selFinancialValue = state.selValue;
selFinancialPeriod = FinancialYear.fromJson(selFinancialValue);
getFinancialPeriodDropDown(context);
}
return MaterialApp(
home: Scaffold(
body: SafeArea(
child: Stack(
children: [
Container(
child: Column(
children: [
SizedBox(height: 3,),
getFinancialPeriodDropDown(context),
SizedBox(height: 3,),
DefaultTabController(
length: 2, // length of tabs
initialIndex: 0,
child: Builder(
builder: (context) {
tabController = DefaultTabController.of(context);
tabController?.addListener(() {
selTab = tabController?.index;
context.read<MyAccountBloc>().add(ChangeTabEvent(tabIndex: tabController?.index));
});
return Column(
children: <Widget>[
....
],
);
},
),
),
],
),
),
Positioned(
bottom: 0,
left: 30,
right: 30,
child: getTabButtons(context),
),
],
)
),
),
);
}
);
}
getFinancialPeriodDropDown(BuildContext context) {
if(financialYearList == null) {
return SizedBox();
}
return setAcademicDropDown(context);
}
setFinancialPeriodDropDown(BuildContext context) {
return SizedBox(
height: 45,
child: DropdownButtonFormField2<dynamic>(
isExpanded: true,
hint: const Text('Select Financial Year',style: TextStyle(fontSize: 14),),
value: selFinancialValue,
items: financialYearList!.map((item) => DropdownMenuItem<dynamic>(
value: item,
child: Text('${dMyFormat.format(yMdFormat.parse(item['startDate']))} :: ${dMyFormat.format(yMdFormat.parse(item['endDate']))}',
style: const TextStyle(fontSize: 14,),
),)).toList(),
validator: (value) {
if (value == null) {
return 'Please select $txtAcaPer.';
}
},
onChanged: (value) {
context.read<MyAccountBloc>().add(UpdateDropDownEvent(selValue: value));
},
onSaved: (value) {},
),
);
}
}
One more thing I need to know is, how can i set the initial(default) value to nothing (ie, like 'Select Financial Year') when I open the page instead of the current financial year??
Edit :
I saw same kind of problem on the below question also
flutter dropdownbutton won't keep answer after scrolling
If you really don't want to lose the selection on orientation change then make the AccountSetup widget as StatefulWidget.
Then your code will be as following
class AccountSetup extends StatefulWidget {
final Company selectedCompany;
AccountSetup({Key? key, required this.selectedCompany}) : super(key: key);
#override
State<AccountSetup> createState() => _AccountSetupState();
}
class _AccountSetupState extends State<AccountSetup> {
FinancialYear selFinancialPeriod = FinancialYear();
dynamic selFinancialValue;
List<dynamic>? financialYearList;
#override
Widget build(BuildContext context) {
context.read<MyAccountBloc>().add(FetchFinancialYearList(widget.selectedCompany.id!));
return BlocBuilder<MyAccountBloc, MyAccountState>(
builder: (context, state) {
if(state is FinancialYearList) {
financialYearList = state.list;
if(financialYearList != null) {
for(dynamic itemFY in financialYearList!) {
if(DateTime.now().isBetween(yMdFormat.parse(itemFY['startDate']), yMdFormat.parse(itemFY['endDate']))) {
selFinancialPeriod = FinancialYear.fromJson(itemFY);
selFinancialValue = itemFY;
break;
}
}
}
getFinancialPeriodDropDown(context);
} else if(state is AccountTabChanged) {
selTab = state.tabIndex;
//tabController!.index = selTab!;
getTabButtons(context);
} else if(state is UpdateDropDown) {
selFinancialValue = state.selValue;
selFinancialPeriod = FinancialYear.fromJson(selFinancialValue);
getFinancialPeriodDropDown(context);
}
return MaterialApp(
scrollBehavior: MyCustomScrollBehavior(),
title: ....,
debugShowCheckedModeBanner: false,
theme: ThemeData(
colorScheme: ColorScheme.fromSwatch().copyWith(
primary: primaryColor,
),),
home: Scaffold(
resizeToAvoidBottomInset: false,
appBar: sizedAppBar,
body: SafeArea(
child: Stack(
children: [
Container(
child: Column(
children: [
SizedBox(height: 3,),
getFinancialPeriodDropDown(context),
SizedBox(height: 3,),
DefaultTabController(
length: 2, // length of tabs
initialIndex: 0,
child: Builder(
builder: (context) {
tabController = DefaultTabController.of(context);
tabController?.addListener(() {
selTab = tabController?.index;
context.read<MyAccountBloc>().add(ChangeTabEvent(tabIndex: tabController?.index));
});
return Column(
children: <Widget>[
Container(
child: TabBar(
controller: tabController,
labelColor: textWhite1,
unselectedLabelColor: Colors.black,
indicatorWeight: 2,
isScrollable: true,
indicatorSize: TabBarIndicatorSize.tab,
indicatorColor: selColor1,
indicatorPadding: EdgeInsets.only(left: 2.0, right: 2.0,bottom: 3.0),
indicator: BoxDecoration(
borderRadius: BorderRadius.circular(5),color: highlightGrey10,shape: BoxShape.rectangle,
),
labelPadding: EdgeInsets.symmetric (horizontal: 1),
tabs: [
Container(
width: mainTabWidth,
height: mainTabHeight,
decoration: BoxDecoration(color: tab1Color,border: Border.all(color: border1Color,width: 2,style: BorderStyle.solid),borderRadius: BorderRadius.circular(5)),
child: Tab(child:Text(tabAccSt1,textAlign: TextAlign.center,style: TextStyle(fontSize: fontSize,fontWeight: FontWeight.bold,),),),
),
Container(
width: mainTabWidth,
height: mainTabHeight,
decoration: BoxDecoration(color: tab2Color,border: Border.all(color: border1Color,width: 2,style: BorderStyle.solid),borderRadius: BorderRadius.circular(5)),
child: Tab(child:Text(tabAccSt2,textAlign: TextAlign.center,style: TextStyle(fontSize: fontSize,fontWeight: FontWeight.bold,),),),
),
],
),
),
Container(
height: 400, //height of TabBarView
decoration: BoxDecoration(
border: Border(top: BorderSide(color: Colors.grey, width: 0.5))
),
child: TabBarView(
controller: tabController,
children: <Widget>[
Container(
child: Column(
children: [
getCompanySites(),
],
),
),
Container(
child: Center(
child: Text('Display Tab 2', style: TextStyle(fontSize: 22, fontWeight: FontWeight.bold)),
),
),
],
),
),
],
);
},
),
),
],
),
),
Positioned(
bottom: 0,
left: 30,
right: 30,
child: getTabButtons(context),
),
],
)
),
),
);
}
);
}
getFinancialPeriodDropDown(BuildContext context) {
if(financialYearList == null) {
return SizedBox();
}
return setAcademicDropDown(context);
}
setFinancialPeriodDropDown(BuildContext context) {
return SizedBox(
height: 45,
child: DropdownButtonFormField2<dynamic>(
isExpanded: true,
hint: const Text('Select Financial Year',style: TextStyle(fontSize: 14),),
value: selFinancialValue,
icon: const Icon(Icons.arrow_drop_down,color: Colors.black45,),
iconSize: 30,
buttonHeight: 60,
buttonPadding: const EdgeInsets.only(left: 20, right: 10),
dropdownDecoration: BoxDecoration(borderRadius: BorderRadius.circular(15),),
items: financialYearList!.map((item) => DropdownMenuItem<dynamic>(
value: item,
child: Text('${dMyFormat.format(yMdFormat.parse(item['startDate']))} :: ${dMyFormat.format(yMdFormat.parse(item['endDate']))}',
style: const TextStyle(fontSize: 14,),
),)).toList(),
validator: (value) {
if (value == null) {
return 'Please select $txtAcaPer.';
}
},
onChanged: (value) {
context.read<MyAccountBloc>().add(UpdateDropDownEvent(selValue: value));
},
onSaved: (value) {},
),
);
}
}
If you not seeing any changes on UI then, you have to call setState method to refresh the UI.
`I am trying to implement a toggle switch in which when I toggle the switch I display two different things but I'm getting an error while I use setstate with my logic or something like that if I remove the setstate from my code the animation starts working again but the logic does not work and I don't get two different outcomes when i toggle between the switch
the code is :
import 'package:flutter/material.dart';
import 'package:sadapay_clone/screens/homepage.dart';
import 'package:sadapay_clone/widgets/physical_card_item.dart';
import 'package:sadapay_clone/widgets/virtual_card_item.dart';
import 'package:toggle_switch/toggle_switch.dart';
class CardScreen extends StatefulWidget {
const CardScreen({super.key});
#override
State<CardScreen> createState() => _CardScreenState();
}
class _CardScreenState extends State<CardScreen>{
// AnimationController _controller;
bool toggle = false;
// #override
// void initState() {
// _controller = AnimationController(vsync: this);
// super.initState();
// }
// void toggleSwitch(int index) {
// if (index == 0) {
// setState(() {
// toggle = true;
// });
// } else if (index == 1) {
// setState(() {
// toggle = false;
// });
// }
// }
void toggleSwitch(int index) {
if (index == 0) {
setState(() {
toggle = true;
});
} else if (index == 1) {
setState(() {
toggle = false;
});
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
const SizedBox(height: 75),
SizedBox(
width: double.infinity,
child: Row(
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
IconButton(
onPressed: () {
Navigator.pop(
context,
MaterialPageRoute(
builder: (context) => const MyHomePage(),
),
);
},
icon: const Icon(Icons.arrow_back_ios),
),
Container(
width: 295,
alignment: Alignment.center,
child: const Text(
'My Cards',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w500,
),
textAlign: TextAlign.center,
),
),
],
),
),
const SizedBox(
height: 20,
),
Container(
alignment: Alignment.center,
height: 40,
width: 365,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20),
color: Colors.grey[300],
),
child: ToggleSwitch(
minHeight: 30,
minWidth: 180.0,
cornerRadius: 20.0,
activeBgColors: const [
[Colors.white],
[Colors.white]
],
activeFgColor: Colors.black,
inactiveBgColor: Colors.grey[300],
inactiveFgColor: Colors.black54,
initialLabelIndex: 0,
totalSwitches: 2,
labels: const ['Virtual', 'Physical'],
radiusStyle: true,
onToggle: (index) {
toggleSwitch(index!);
},
// onToggle: (index) {
// setState(() {
// toggle = index == 0;
// });
// },
),
),
toggle ? VirtualCard() : PhysicalCard(),
],
),
);
}
}
I tried using setstate logic inside the function rather than using it inside the onchanged property but still, the logic was working I was seeing two different outcomes when I pressed the switch but the animation was not working`
The issue is, while we are calling setState, the build method is trigger and setting initialLabelIndex to 0 again, you can do a check here,
class _CardScreenState extends State<CardScreen> {
bool toggle = false;
void toggleSwitch(int index) {
if (index == 0) {
setState(() {
toggle = true;
});
} else if (index == 1) {
setState(() {
toggle = false;
});
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
Container(
alignment: Alignment.center,
height: 40,
width: 365,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20),
color: Colors.grey[300],
),
child: ToggleSwitch(
minHeight: 30,
minWidth: 180.0,
cornerRadius: 20.0,
activeBgColors: const [
[Colors.white],
[Colors.white]
],
activeFgColor: Colors.black,
inactiveBgColor: Colors.grey[300],
inactiveFgColor: Colors.black54,
initialLabelIndex: toggle ? 0 : 1,
totalSwitches: 2,
labels: ['Virtual', 'Physical'],
radiusStyle: true,
onToggle: (index) {
toggleSwitch(index!);
},
),
),
toggle ? Text("VirtualCard") : Text("PhysicalCard"),
],
),
);
}
}
I have such a column, consisting as it were of the title and the content created via List.generate. How I can create an animation of collapsing this list into a title, actually by clicking on the title itself?
Column(
children: [
GestureDetector(
onTap: () {},
child: Container(
color: Theme.of(context).sliderTheme.inactiveTrackColor,
width: MediaQuery.of(context).size.width,
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 3),
child: Center(
child: Text('${category.name}',
style: Theme.of(context).textTheme.headline6.copyWith(
fontSize: 16,
color: Theme.of(context).selectedRowColor)),
),
),
),
),
Column(
children: List.generate(category.items.length, (index) {
return Container(
decoration: BoxDecoration(
border: (index + 1) != category.items.length
? Border(
bottom: BorderSide(
color: Style.inactiveColorDark.withOpacity(1.0),
width: 0.5))
: null),
child: Padding(
padding:
const EdgeInsets.symmetric(vertical: 12.0, horizontal: 24),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
Container(
width: 12,
height: 12,
decoration: BoxDecoration(
color: Color(int.parse(category.color)),
shape: BoxShape.circle),
),
SizedBox(
width: 12,
),
Text('${category.items[index].description}',
style: Theme.of(context)
.primaryTextTheme
.headline1
.copyWith(fontSize: 16)),
],
),
SwitchButton(
isActive: category.items[index].isAdded,
activeColor:
Theme.of(context).sliderTheme.activeTrackColor,
inactiveColor:
Theme.of(context).sliderTheme.inactiveTrackColor,
activeCircleColor:
Theme.of(context).sliderTheme.activeTickMarkColor,
inactiveCircleColor:
Theme.of(context).sliderTheme.inactiveTickMarkColor,
turnOn: () {
ChosenFeeling removingElement = ChosenFeeling(
id: 000,
isAdded: false,
);
// If chosen list is empty
if (chosenfeelings.isEmpty) {
chosenfeelings.add(ChosenFeeling(
isAdded: true, id: category.items[index].id));
} else {
// If user tap on switchButton 2 times
chosenfeelings.removeWhere((element) {
if (element.id != null &&
element.id == category.items[index].id) {
removingElement = element;
}
return _isNeedToRemoveWhenOn(
currentItem: category.items[index],
listItem: element,
);
});
// If list isn`t empty and chosen item isn`t in list
if (category.items[index].id != removingElement.id) {
chosenfeelings.add(ChosenFeeling(
id: category.items[index].id,
isAdded: true,
));
}
}
},
turnOff: () {
ChosenFeeling removingElement = ChosenFeeling(
id: 000,
isAdded: false,
);
if (chosenfeelings.isEmpty) {
chosenfeelings.add(ChosenFeeling(
id: category.items[index].id,
isAdded: false,
));
} else {
// If user tap on switchButton 2 times
chosenfeelings.removeWhere((element) {
if (element.id != null &&
element.id == category.items[index].id) {
removingElement = element;
}
return _isNeedToRemoveWhenOff(
currentItem: category.items[index],
listItem: element,
);
});
// If list isn`t empty and chosen item isn`t in list
if (category.items[index].id != removingElement.id) {
chosenfeelings.add(ChosenFeeling(
id: category.items[index].id,
isAdded: false,
));
}
}
},
)
],
),
),
);
}),
)
],
);
The easiest way is to use expandable package for this purpose:
https://pub.dev/packages/expandable
Create expandable controller outside of build method
final ExpandableController _expandableController = ExpandableController();
And then use it like this:
ExpandableNotifier(
controller: _expandableController,
child: Column(
children: [
Expandable(
collapsed: ExpandableButton(
child: titleWidget(),
),
expanded: Column(
children: [
ExpandableButton(
child: titleWidget(),
),
list(),
]
),
),
],
),
);
Don't forget to dispose controller after using it
#override
void dispose() {
_expandableController.dispose();
super.dispose();
}
Hope this helps. You can also create your own animation with a little research
I am new to flutter and trying to solve the below issue, in the first screenshot I have labelStart line 127 value as 1.21, when i pass that value using the variable i get renderflex error, if i am using hardcoded i don't get any error. Not sure what i am doing wrong. I tried adding Expanded after reading few posts online but that didn't help. Can someone please guide me on what mistake i am doing here?
class _ListPageState extends State<ListPage> with SingleTickerProviderStateMixin {
List<MusicModel> _list;
var _value;
int _playId;
String _playURL;
bool isPlaying = false;
String _startTime;
String _endTime;
AnimationController _controller;
AudioPlayer _audioPlayer = AudioPlayer();
#override
void initState() {
_playId = 0;
_list = MusicModel.list;
_controller = AnimationController(vsync: this,duration: Duration(microseconds: 250));
_value = 0.0;
_startTime="0.0";
_endTime="0.0";
super.initState();
_audioPlayer.onAudioPositionChanged.listen((Duration duration) {
setState(() {
_startTime = duration.toString().split(".")[0];
});
});
_audioPlayer.onDurationChanged.listen((Duration duration) {
setState(() {
_endTime = duration.toString().split(".")[0];
});
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
elevation: 0,
backgroundColor: AppColors.mainColor,
centerTitle: true,
title: Text("Skin - Flume",
style: TextStyle(color: AppColors.styleColor),),
),
backgroundColor: AppColors.mainColor,
body: Stack(
children: <Widget>[
Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(24.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
CustomButtonWidget(
child: Icon(
Icons.favorite,
color: AppColors.styleColor,
),
size: 50,
onTap: (){
},
),
CustomButtonWidget(
image: 'assets/logo.jpg',
size: 100,
borderWidth: 5,
onTap: (){
Navigator.of(context).push(
MaterialPageRoute(builder: (_) => DetailPage(),
),
);
},
),
CustomButtonWidget(
child: Icon(
Icons.menu,
color: AppColors.styleColor,
),
size: 50,
onTap: (){
Navigator.of(context).push(
MaterialPageRoute(builder: (_) => HomePage(),
),
);
},
)
],
),
),
//Progress bar section
// Expanded(child: SizedBox()),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 24),
child: CustomProgressWidget(
value: _value,
labelStart: "1.21",
labelend: "2.34",
),
),
// Expanded(child: SizedBox()),
Expanded( //This is added so we can see overlay else this will be over button
child: ListView.builder(
physics: BouncingScrollPhysics(),//This line removes the dark flash when you are at the begining or end of list menu. Just uncomment for
itemCount: _list.length,
padding: EdgeInsets.all(12),
itemBuilder: (context,index){
return GestureDetector(
onTap: (){
Navigator.of(context).push(
MaterialPageRoute(builder: (_) => DetailPage(),
),
);
},
child: AnimatedContainer(
duration: Duration(milliseconds: 500),
//This below code will change the color of sected area or song being played.
decoration: BoxDecoration(
color: _list[index].id == _playId
? AppColors.activeColor
: AppColors.mainColor,
borderRadius: BorderRadius.all(
Radius.circular(20),
),
),
//End of row color change
child: Padding(
padding: const EdgeInsets.all(16), //This will all padding around all size
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, //This will allign button to left, else button will be infront of name
children: <Widget>[
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
_list[index].title,
style: TextStyle(
color: AppColors.styleColor,
fontSize: 16,
),
),
Text(
_list[index].album,
style: TextStyle(
color: AppColors.styleColor.withAlpha(90),
fontSize: 16,
),
),
],
),
CustomButtonWidget( //This is Play button functionality on list page.
child: Icon(
_list[index].id == _playId
? Icons.pause
: Icons.play_arrow,
color: _list[index].id == _playId
? Colors.white
: AppColors.styleColor,
),
size: 50,
isActive: _list[index].id == _playId,
onTap: () async {
if (isPlaying){
if (_playId == _list[index].id){
int status = await _audioPlayer.pause();
if (status == 1){
setState(() {
isPlaying = false;
});
}
} else {
_playId = _list[index].id;
_playURL = _list[index].songURL;
_audioPlayer.stop();
int status = await _audioPlayer.play(_playURL);
if (status == 1){
setState(() {
isPlaying = true;
});
}
}
// int status = await _audioPlayer.pause();
// if (status == 1){
// setState(() {
// isPlaying = false;
// });
// }
} else {
_playId = _list[index].id;
_playURL = _list[index].songURL;
int status = await _audioPlayer.play(_playURL);
if (status == 1){
setState(() {
isPlaying = true;
});
}
} // String filePath = await FilePicker.getFilePath();
},
// onTap: (){
// setState(() {
// _playId = _list[index].id;
// });
// },
)
],
),
),
),
);
},
),
)
],
),
Align(
alignment: Alignment.bottomCenter,
child: Container(
height: 50,
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
AppColors.mainColor.withAlpha(0),
AppColors.mainColor,
],
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
)
),
),
)
],
),
);
}
}
After changing line 1.27 with a dynamic value variable.
You have to wrap you Column in a scrollview like SingleChildScrollView or ListView. This error occurs when the items in column take more space than available.
I had the same problem, and my problem was my variable extracted of my state it took too long in show and showed first the widget after the variable.
In my case use Getx, and fixed for this way:
My old code:
GetX<UserController>(
builder: (_){
return Text(_.user.name);
}
},
),
My new code
GetX<UserController>(
builder: (_){
if(_.user.email != null){
return Text(_.user.name);
}else{
return Text('Loading....');
}
},
),
I have encountered a problem which I cannot move between pages when I click on the icons, but I am setting the default page is the home page and when the application is running it only appears and I cannot move to other pages using the navigation bar.
Navigation Bar File:
class NavigationBar extends StatefulWidget {
#override
_NavigationBarState createState() => _NavigationBarState();
}
class _NavigationBarState extends State<NavigationBar> {
int pageIndex = 0;
final Search _search = Search();
final UserNoti _userNoti = UserNoti();
final HomePage _homePage = HomePage();
final MyProfile _myProfile = MyProfile();
final MyMajor _mymajor = MyMajor();
Widget _showPage = new HomePage();
Widget _pageChooser(int page)
{
switch (page)
{
case 0:
return _search;
break;
case 1:
return _userNoti;
break;
case 2:
return _homePage;
break;
case 3:
return _myProfile;
break;
case 4:
return _mymajor;
break;
default:
return _homePage;
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
bottomNavigationBar: Container(
decoration: new BoxDecoration(
// borderRadius: BorderRadius.circular(28.0),
gradient: LinearGradient(
begin: Alignment.topRight,
end: Alignment.bottomLeft,
colors: [Colors.deepPurple, Colors.purple]),
boxShadow: [
BoxShadow(
color: Colors.black,
blurRadius: 25.0, // soften the shadow
spreadRadius: -10.0, //extend the shadow
)
],
),
child: CurvedNavigationBar(
index: 2,
color: Color.fromRGBO(58, 66, 86, 1.0),
buttonBackgroundColor: Colors.purple.withOpacity(0.6),
backgroundColor: Colors.transparent,
animationDuration: Duration(milliseconds: 200),
animationCurve: Curves.bounceInOut,
items: <Widget>[
Icon(Icons.search, size: 32, color: Colors.white,),
Icon(Icons.notifications_active, size: 32, color: Colors.white,),
Icon(Icons.home, size: 40, color: Colors.white,),
Icon(Icons.chat, size: 32, color: Colors.white,),
Icon(Icons.school, size: 32, color: Colors.white,),
],
onTap: (int tappedPage){
_showPage = _pageChooser(tappedPage);
},),
),
body: Container(
color: Colors.transparent,
child: Center(
child: _showPage,
),
));
}
}
Now I will put one code for one of those pages for example User Notification Page:
class UserNoti extends StatefulWidget {
final String myuid,majname,uniname;
UserNoti({Key key, this.myuid,this.majname,this.uniname}) : super(key: key);
#override
_UserNotiState createState() => _UserNotiState();
}
class _UserNotiState extends State<UserNoti> {
String myapplang = null;
var my_uid;
var myDoc;
bool loading = false;
#override
void initState() {
super.initState();
checkLang();
}
#override
Widget build(BuildContext context) {
return loading
? LoadingMain()
: Directionality(
textDirection: myapplang == 'ar' ? TextDirection.rtl : TextDirection.ltr,
child: Scaffold(
backgroundColor: Color.fromRGBO(58, 66, 86, 1.0),
appBar: GradientAppBar(
backgroundColorStart: Colors.deepPurple,
backgroundColorEnd: Colors.purple,
title: Text(
myapplang == 'ar' ? "الإشعارات" : "Notifications" ,
style: TextStyle(fontSize: 16,fontWeight: FontWeight.bold,fontFamily: myapplang == 'ar' ? 'Tajawal' : 'BalooDa2' ),),
),
body: ListView(
children: <Widget>[
Column(
children: <Widget>[
SizedBox(height: 30.0,),
Container(
width: MediaQuery.of(context).size.width*0.85,
child: Stack(
children: <Widget>[
StreamBuilder(
stream: Firestore.instance.collection('users').document(myDoc).collection('Notifications').orderBy('date',descending: true).snapshots(),
builder: (context, snapshot){
if(!snapshot.hasData)
{
const Text('Loading...');
}
else{
return ListView.builder(
physics: NeverScrollableScrollPhysics(),
shrinkWrap: true,
scrollDirection: Axis.vertical,
itemCount: snapshot.data.documents.length,
itemBuilder: (context, index)
{
DocumentSnapshot mypost = snapshot.data.documents[index];
return Stack(
children: <Widget>[
FadeAnimation(0.2,Column(
children: <Widget>[
Container(
decoration: BoxDecoration(borderRadius:
BorderRadius.circular(15.0),color: Color.fromRGBO(64, 75, 96, .9),
boxShadow: [
BoxShadow(
color: Colors.black,
blurRadius: 30.0, // soften the shadow
spreadRadius: -25.0, //extend the shadow
)
],),
child:InkWell(
onTap: () {
// setState(() => loading = true);
if(mypost['type'] == 'comment'){
if(mypost['location'] == 'Majors'){
Firestore.instance
.document('/Majors/${widget.majname}/Posts/${mypost['pdoc']}')
.get()
.then((val) {
if(val.data['pid'] != null && val.data['text'] != null && val.data['uid'] != null && val.data['uimg'] != null && val.data['uname'] != null && val.data['uname'] != null)
{
Navigator.push(context, new MaterialPageRoute(builder: (context) => new Comments(pid: val.data['pid'], text: val.data['text'],img: val.data['img'],uid: val.data['uid'], uimg: val.data['uimg'],uname: val.data['uname'],majname: widget.majname,pdoc:mypost['pdoc'] )));
}
else {
showWraning(context,myapplang == 'ar' ? "حدثت مشكلة، قد يكون الإشعار محذوف أو مشكلة بالإنترنت، حاول لاحقًا." : "There is a problem, maybe notification has been deleted or problem with internet connection, try again later.");
}
/*
*/
}).catchError((e) {
print(e);
});
}
else {
Firestore.instance
.document('/Universities/${widget.uniname}/Posts/${mypost['pdoc']}')
.get()
.then((val) {
if(val.data['pid'] != null && val.data['text'] != null && val.data['uid'] != null && val.data['uimg'] != null && val.data['uname'] != null && val.data['uname'] != null)
{
Navigator.push(context, new MaterialPageRoute(builder: (context) => new U_Comments(pid: val.data['pid'], text: val.data['text'],img: val.data['img'],uid: val.data['uid'], uimg: val.data['uimg'],uname: val.data['uname'],uniname: widget.uniname,pdoc:mypost['pdoc'] )));
}
else {
showWraning(context,myapplang == 'ar' ? "حدثت مشكلة، قد يكون الإشعار محذوف أو مشكلة بالإنترنت، حاول لاحقًا." : "There is a problem, maybe notification has been deleted or problem with internet connection, try again later.");
}
/*
*/
}).catchError((e) {
print(e);
});
}
}
else {
print('not comment!');
}
},
child: ListTile(
contentPadding: EdgeInsets.symmetric(horizontal: 20.0, vertical: 10.0),
leading: ClipRRect(
borderRadius: BorderRadius.circular(35.0),
child: Icon(Icons.insert_comment,color: Colors.deepPurple,size: 40,),
),
title: Column(
children: <Widget>[
Text(
"${mypost['text']}",
style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold,fontFamily: 'BalooDa2'),
textAlign: TextAlign.left,
),
SizedBox(height: 10.0,),
],
),
// subtitle: Text("Intermediate", style: TextStyle(color: Colors.white)),
trailing:
Icon(Icons.keyboard_arrow_right, color: Colors.white, size: 30.0)),
),
),
SizedBox(height: 20.0,),
],
),),
],
);
}
);
}
return Container(
height: 0.0,
width: 0.0,
);
},
),
],
),
),
],
),
],
),
),
);
}
I removed a few codes that had no need.
Note when running the application the active page is Homepage()
Right now you are setting your _showPage variable with a new page, but not in a way that will force your app to re-render the widget tree. Try using SetState in your tap function to do this.
onTap: (int tappedPage){
setState(() {
_showPage = _pageChooser(tappedPage);
});
}),