How to make the activity stack null in flutter? - flutter

I have 4 walkthrough screens, on reaching the ending of the screens when i go to the homepage of my app which is named as TestScreen here,when i press the back button in my phone i again go back to the walkthrough pages which i dont want and it throws an exception too ("Failed assertion: line 1554 pos 12: '!_debugLocked': is not true."). So i was thinking if i make the activity stack null after coming to TestScreen it might work but i am not able to do so. Please help me.
Main.dart
library flutter_walkthrough;
import 'package:flutter/material.dart';
import 'package:comp_apps/walkthrough.dart';
void main(){
runApp(MyApp());
}
class MyApp extends StatelessWidget {
final List<Walkthrough> list = [
Walkthrough(
title: "Title 1",
content: "Content 1",
imageIcon: Icons.restaurant_menu,
),
Walkthrough(
title: "Title 2",
content: "Content 2",
imageIcon: Icons.search,
),
Walkthrough(
title: "Title 3",
content: "Content 3",
imageIcon: Icons.shopping_cart,
),
Walkthrough(
title: "Title 4",
content: "Content 4",
imageIcon: Icons.verified_user,
),
];
#override
Widget build(BuildContext context) {
return MaterialApp(
home: IntroScreen(list, MaterialPageRoute(builder: (context)=>
TestScreen())).,
);
}
}
class TestScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Hello"),
automaticallyImplyLeading: false,
),
);
}
}
class IntroScreen extends StatefulWidget {
final List<Walkthrough> walkthroughList;
final MaterialPageRoute pageRoute;
IntroScreen(this.walkthroughList, this.pageRoute);
void skipPage(BuildContext context) {
Navigator.push(context, pageRoute);
}
#override
_IntroScreenState createState() => _IntroScreenState();
}
class _IntroScreenState extends State<IntroScreen> {
final PageController controller = new PageController();
int currentPage = 0;
bool lastPage = false;
void _onPageChanged(int page) {
setState(() {
currentPage = page;
if (currentPage == widget.walkthroughList.length - 1) {
lastPage = true;
} else {
lastPage = false;
}
});
}
#override
Widget build(BuildContext context) {
return Container(
color: Color(0xFFEEEEEE),
padding: const EdgeInsets.all(10.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Expanded(
child: Container(),
flex: 1,
),
Expanded(
flex: 3,
child: PageView(
children: widget.walkthroughList,
controller: controller,
onPageChanged: _onPageChanged,
),
),
Expanded(
flex: 1,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.end,
children: <Widget>[
FlatButton(
child: Text(
lastPage ? "" : "SKIP",
style: TextStyle(
color: Colors.black,
fontWeight: FontWeight.bold,
fontSize: 16.0),
),
onPressed: () => lastPage ? null : widget.skipPage(context),
),
FlatButton(
child: Text(
lastPage ? "GOT IT" : "NEXT",
style: TextStyle(
color: Colors.black,
fontWeight: FontWeight.bold,
fontSize: 16.0,
),
),
onPressed: () => lastPage
? widget.skipPage(context)
: controller.nextPage(
duration: Duration(milliseconds: 300),
curve: Curves.easeIn),
)
],
),
)
],
),
);
}
}
Walkthrough.dart
import 'package:flutter/material.dart';
class Walkthrough extends StatefulWidget {
final title;
final content;
final imageIcon;
final imagecolor;
Walkthrough({this.title, this.content, this.imagecolor, this.imageIcon});
#override
_WalkthroughState createState() => _WalkthroughState();
}
class _WalkthroughState extends State<Walkthrough>
with SingleTickerProviderStateMixin {
Animation animation;
AnimationController animationController;
#override
void initState() {
// TODO: implement initState
super.initState();
animationController = AnimationController(vsync: this,duration:
Duration(milliseconds: 500));
animation = Tween(
begin: -250.0, end: 0.0).animate(CurvedAnimation(parent:
animationController, curve: Curves.easeInOut));
animation.addListener(() => setState(() {}));
animationController.forward();
}
#override
void dispose() {
// TODO: implement dispose
super.dispose();
animationController.dispose();
}
#override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.all(20.0),
child: Material(
animationDuration: Duration(milliseconds: 500),
elevation: 2.0,
borderRadius: BorderRadius.all(Radius.circular(5.0)),
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Transform(
transform: Matrix4.translationValues(animation.value, 0.0, 0.0),
child: Text(widget.title,style: TextStyle(
fontSize: 20.0,
fontWeight: FontWeight.bold,
color: Colors.black
),),
),
Transform(
transform: Matrix4.translationValues(animation.value, 0.0, 0.0),
child: Text(widget.content,
softWrap: true,
textAlign: TextAlign.center,
style: TextStyle(
fontWeight: FontWeight.normal,
fontSize: 15.0,
color: Colors.black,
),),
),
Icon(
widget.imageIcon,
size: 100.0,
color: widget.imagecolor,
)
],
),
),
);
}
}

Related

AutomaticKeepAliveClientMixin not working with CupertinoSwitch

I have page where build ListView with CupertinoSwitch.
class NewsPageRegistr extends StatefulWidget {
NewsPageRegistr({Key key, this.model}) : super(key: key);
RegistrationModel model;
#override
_NewsPageRegistrState createState() => _NewsPageRegistrState();
}
class _NewsPageRegistrState extends State<NewsPageRegistr>
with AutomaticKeepAliveClientMixin<NewsPageRegistr> {
#override
bool get wantKeepAlive => true;
var responseV;
List<DigitalNews> digitalNews = [];
#override
void dispose() {
super.dispose();
}
#override
void initState() {
fitchData();
super.initState();
}
fitchData() async {
responseV = await apiRegistrationDataList(DropMenuMode.news);
for (var values in responseV) {
digitalNews.add(DigitalNews.fromJson(values));
}
}
#override
Widget build(BuildContext context) {
super.build(context);
return SingleChildScrollView(
child: SafeArea(
minimum: const EdgeInsets.fromLTRB(25, 0, 25, 0),
child: Column(
mainAxisSize: MainAxisSize.max,
children: <Widget>[
SizedBox(
width: 400,
height: 85,
child: Text(
"Ich möchte folgende Newsletter abonnieren",
textAlign: TextAlign.left,
style: GeneralStyles.titleStyle,
),
),
ListView.builder(
//padding: const EdgeInsets.symmetric(vertical: 8),
physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true,
itemCount: digitalNews.length,
itemBuilder: (context, index) {
return CheckWidget(
title: digitalNews[index].displayName,
subtitle: digitalNews[index].displayDescription,
registrationName: digitalNews[index].name,
index: index,
model: widget.model,
);
}),
Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
OutlinedButton(
style: OutlinedButton.styleFrom(
backgroundColor: GeneralStyles.petrolColor,
),
onPressed: () {
setState(() {});
widget.model.setCurrentPage(2);
},
child: const Text(
"Weiter",
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.w300,
fontFamily: 'FiraSans'),
)),
],
),
),
],
),
),
],
),
),
);
}
}
I use AutomaticKeepAliveClientMixin but it not working when i jump to next page (on PageView).
I add AutomaticKeepAliveClientMixin on main widget when call PageView, add to CheckWidget and to NewsPageRegistr widget but it not working. when i jump to other page on PageView List NewsPageRegistr state is dispose.
Code of widget:
class CheckWidget extends StatefulWidget {
RegistrationModel model;
bool checker = false;
final Function(String) onChanged;
final String title;
final String subtitle;
final String registrationName;
final int index;
CheckWidget({Key key, this.onChanged, this.title, this.subtitle, this.registrationName, this.index,this.model}) : super(key: key);
#override
State<CheckWidget> createState() => _CheckWidgetState();
}
class _CheckWidgetState extends State<CheckWidget> {
#override
Widget build(BuildContext context) {
return Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Transform.scale(
alignment: Alignment.centerLeft,
scale: 0.8,
child: CupertinoSwitch(
value: widget.checker,
activeColor: GeneralStyles.petrolColor,
onChanged: (bool value) {
setState(() {
widget.checker = value;
if(value == true) {
widget.model.newsPapers[widget.index] = widget.registrationName;
}else {
widget.model.newsPapers[widget.index] = '';
}
});
},
),
),
Expanded(
child: GestureDetector(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(widget.title.replaceFirst("Newsletter", "").trim(), style:TextStyle(
color: GeneralStyles.blackColor,
fontSize: 15,
fontFamily: 'FiraSans',
fontWeight: FontWeight.w400,
)),
Text(widget.subtitle, style:TextStyle(
color: GeneralStyles.blackColor,
fontSize: 13,
fontFamily: 'FiraSans',
fontWeight: FontWeight.w300,
)),
],
),
onTap: (){
setState(() {
if(widget.checker == false) {
widget.checker = true;
widget.model.newsPapers[widget.index] = widget.registrationName;
}else {
widget.checker = false;
widget.model.newsPapers[widget.index] = '';
}
});
},
),
),
]
),
);
}
}

How to separate different widgets alongside another widget?

This is my problem:
I want to achieve this design:
How can I separate one to each other, first the homePageTimerUI and below the countDownTimer
?
This is my code:
homePageTimerUI.dart
class HomePageTimerUI extends StatefulWidget {
const HomePageTimerUI({Key? key}) : super(key: key);
#override
State<HomePageTimerUI> createState() => _HomePageTimerUIState();
}
class _HomePageTimerUIState extends State<HomePageTimerUI> {
#override
Widget build(BuildContext context) {
return SizedBox(
height: double.maxFinite,
width: double.infinity,
child: DefaultTabController(
length: 3,
child: Scaffold(
appBar: AppBar(
elevation: 0,
backgroundColor: Colors.transparent,
bottom: PreferredSize(
preferredSize: const Size.fromHeight(40),
child: Container(
color: Colors.transparent,
child: SafeArea(
child: Column(
children: <Widget>[
TabBar(
indicator: const UnderlineTabIndicator(
borderSide: BorderSide(
color: Color(0xff3B3B3B), width: 4.0),
insets:
EdgeInsets.fromLTRB(12.0, 12.0, 12.0, 11.0)),
indicatorWeight: 15,
indicatorSize: TabBarIndicatorSize.label,
labelColor: const Color(0xff3B3B3B),
labelStyle: const TextStyle(
fontSize: 12,
letterSpacing: 1.3,
fontWeight: FontWeight.w500),
unselectedLabelColor: const Color(0xffD7D7D7),
tabs: const [
Tab(
text: "POMODORO",
icon: Icon(Icons.work_history, size: 40),
),
Tab(
text: "SHORT BREAK",
icon: Icon(Icons.ramen_dining, size: 40),
),
Tab(
text: "LONG BREAK",
icon: Icon(Icons.battery_charging_full_rounded,
size: 40),
),
])
],
),
),
),
),
),
body: TabBarView(
children: const <Widget>[
// Center(
// child: StartPomodoro(),
// ),
// Center(
// child: ShortBreak(),
// ),
// Center(child: LongBreak()),
],
),
),
),
);
}
}
countDownTimer.dart
class CountDownTimer extends StatefulWidget {
#override
_CountDownTimerState createState() => _CountDownTimerState();
}
class _CountDownTimerState extends State<CountDownTimer>
with TickerProviderStateMixin {
late AnimationController controller;
String get timerString {
Duration duration = controller.duration! * controller.value;
return '${duration.inMinutes}:${(duration.inSeconds % 60).toString().padLeft(2, '0')}';
}
#override
void initState() {
super.initState();
controller = AnimationController(
vsync: this,
duration: Duration(seconds: 5),
);
}
#override
Widget build(BuildContext context) {
ThemeData themeData = Theme.of(context);
return Scaffold(
backgroundColor: Colors.white10,
body: AnimatedBuilder(
animation: controller,
builder: (context, child) {
return Stack(
children: <Widget>[
Align(
alignment: Alignment.bottomCenter,
child: Container(
color: Colors.amber,
height:
controller.value * MediaQuery.of(context).size.height,
),
),
Padding(
padding: EdgeInsets.all(8.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Expanded(
child: Align(
alignment: FractionalOffset.center,
child: Stack(
children: <Widget>[
CustomPaint(
painter: CustomTimerPainter(
animation: controller,
backgroundColor: Colors.white,
color: themeData.indicatorColor,
)),
Align(
alignment: FractionalOffset.center,
child: Column(
mainAxisAlignment:
MainAxisAlignment.spaceEvenly,
crossAxisAlignment:
CrossAxisAlignment.center,
children: <Widget>[
Text(
timerString,
style: TextStyle(
fontSize: 112.0,
color: Colors.white),
),
],
),
),
],
),
),
),
Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
AnimatedBuilder(
animation: controller,
builder: (context, child) {
return FloatingActionButton.extended(
onPressed: () {
if (controller.isAnimating)
controller.stop();
else {
controller.reverse(
from: controller.value == 0.0
? 1.0
: controller.value);
}
},
icon: Icon(controller.isAnimating
? Icons.pause
: Icons.play_arrow),
label: Text(
controller.isAnimating ? "Pause" : "Play"));
}),
],
),
],
),
),
],
);
}),
);
}
}
class CustomTimerPainter extends CustomPainter {
CustomTimerPainter({
required this.animation,
required this.backgroundColor,
required this.color,
}) : super(repaint: animation);
final Animation<double> animation;
final Color backgroundColor, color;
#override
void paint(Canvas canvas, Size size) {
Paint paint = Paint()
..color = backgroundColor
..strokeWidth = 10.0
..strokeCap = StrokeCap.butt
..style = PaintingStyle.stroke;
canvas.drawCircle(size.center(Offset.zero), size.width / 2.0, paint);
paint.color = color;
double progress = (1.0 - animation.value) * 2 * math.pi;
canvas.drawArc(Offset.zero & size, math.pi * 1.5, -progress, false, paint);
}
#override
bool shouldRepaint(CustomTimerPainter old) {
return animation.value != old.animation.value ||
color != old.color ||
backgroundColor != old.backgroundColor;
}
}
This is my own attempt to solve the issue, but didn't work
class StackedPages extends StatelessWidget {
const StackedPages({super.key});
#override
Widget build(BuildContext context) {
return Column(
children: [
HomePageTimerUI(),
CountDownTimer(),
],
);
}
}
How can achieve this design?
Thank you for any help you can offer
Are you asking for Stack() widget?
You want to replace one for another?
You could use AnimatedSwitcher().
AnimatedSwitcher(
duration: const Duration(seconds: 1), child: MyWidget());
Where MyWidget() is asigned to the widget you want to show via SetState(). The widgets are switched with a nice animation.
If you need to have one Widget placed one above the other in the vertical axis, you should try to remove the scaffold and the SizedBox with infity values.
if this class can't stack.
Just using static type.
add these codes at the same file with CountDownTimer.
static const Widget countDownTimer = CountDownTimer();
...
// class CountDownTimer
Then, you must import this file and call this widget by this parameter.
example
#override
Widget build(BuildContext context) {
return Column(
children: [
HomePageTimerUI(),
countDownTimer,
],
);
}
but you don't need to use StackPages just put countDownTimer in your pages.

flutter scrolling does not work: ListView and SingleChildScrollView

I have been reading solutions for making my screen scroll without success. Below is my code:
'''
class WelcomeScreen extends StatefulWidget {
static const String id = 'welcome_screen';
#override
_WelcomeScreenState createState() => _WelcomeScreenState();
}
class _WelcomeScreenState extends State<WelcomeScreen>
with SingleTickerProviderStateMixin {
AnimationController controller;
Animation animation;
#override
void initState() {
super.initState();
controller =
AnimationController(duration: Duration(seconds: 1), vsync: this);
animation = ColorTween(begin: Colors.blueGrey, end: Colors.white)
.animate(controller);
controller.forward();
controller.addListener(() {
setState(() {});
});
}
#override
void dispose() {
controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: animation.value,
body: Padding(
padding: EdgeInsets.symmetric(horizontal: 24.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Row(
children: <Widget>[
Hero(
tag: 'logo',
child: Container(
child: Image.asset('images/logo.png'),
height: 60.0,
),
),
TypewriterAnimatedTextKit(
text: ['Tonnah'],
textStyle: TextStyle(
fontSize: 45.0,
fontWeight: FontWeight.w900,
),
),
],
),
SizedBox(
height: 48.0,
),
RoundedButton(
title: 'Log In',
colour: Colors.lightBlueAccent,
onPressed: () {
Navigator.pushNamed(context, LoginScreen.id);
},
),
RoundedButton(
title: 'Register',
colour: Colors.blueAccent,
onPressed: () {
Navigator.pushNamed(context, RegistrationScreen.id);
},
),
],
),
),
);
}
}
'''
I have tried ListView and SingleChildScrollView but nothing works. This is a registration page that I will use to capture information about the user. I don't know if this is the best way of doing this or if I should be getting the user to fill out a profile after they register with their email and password.
I am new to flutter and mobile app development so please help?
TIA
Just wrap your Column with SingleChildScrollView()

Flutter - Expandable text not working properly with overflow property

Basically I want to achieve exactly the same thing as Flutter: How to hide or show more text within certain length.
Here is my code snippet.
LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
final TextSpan span = TextSpan(
text: text,
style: TextStyle(
fontSize: 13,
),
);
final TextPainter textPainter = TextPainter(
text: span,
maxLines: 1,
ellipsis: '...',
textDirection: TextDirection.ltr,
);
textPainter.layout(maxWidth: constraints.maxWidth);
if (textPainter.didExceedMaxLines)
return Row(
crossAxisAlignment: _basicInformationIsExpanded
? CrossAxisAlignment.end
: CrossAxisAlignment.start,
children: <Widget>[
Expanded(
child: Text(
text,
style: TextStyle(
fontSize: 13,
),
maxLines: _isExpanded ? null : 1,
//overflow: TextOverflow.ellipsis,
),
),
GestureDetector(
child: _isExpanded
? Icon(
Icons.expand_less,
)
: Icon(
Icons.expand_more,
),
onTap: () {
setState(() => _isExpanded =
!_isExpanded);
},
),
],
);
else
return Text(
text,
style: TextStyle(
fontSize: 13,
),
);
}),
The weird thing is if I comment overflow: TextOverflow.ellipsis,, everything is fine. But I need to show the ellipsis and if I add that line, the text doesn't expand when I click the icon.
Can anyone help me with it? Thanks.
You can copy paste run full code below
You can set overflow based on _isExpanded
overflow: _isExpanded ? null : TextOverflow.ellipsis,
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;
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>[
Container(
width: 200,
child: ExpandText(
text: "long string" * 10,
)),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
class ExpandText extends StatefulWidget {
String text;
ExpandText({this.text});
#override
_ExpandTextState createState() => _ExpandTextState();
}
class _ExpandTextState extends State<ExpandText> {
bool _isExpanded = false;
bool _basicInformationIsExpanded = true;
#override
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
final TextSpan span = TextSpan(
text: widget.text,
style: TextStyle(
fontSize: 13,
),
);
final TextPainter textPainter = TextPainter(
text: span,
maxLines: 1,
ellipsis: '...',
textDirection: TextDirection.ltr,
);
textPainter.layout(maxWidth: constraints.maxWidth);
if (textPainter.didExceedMaxLines) {
print("exceed");
return Row(
crossAxisAlignment: _basicInformationIsExpanded
? CrossAxisAlignment.end
: CrossAxisAlignment.start,
children: <Widget>[
Expanded(
flex: 1,
child: Text(
widget.text,
style: TextStyle(
fontSize: 13,
),
maxLines: _isExpanded ? null : 1,
overflow: _isExpanded ? null : TextOverflow.ellipsis,
),
),
GestureDetector(
child: _isExpanded
? Icon(
Icons.expand_less,
)
: Icon(
Icons.expand_more,
),
onTap: () {
setState(() => _isExpanded = !_isExpanded);
},
),
],
);
} else {
print("not exceed");
return Text(
widget.text,
style: TextStyle(
fontSize: 13,
),
);
}
});
}
}
A long ago i stumbled onto same thing, surely using these widget's is a way to do this,
but here is the code which i wrote and its totally customizable.
You can change the limit variable to use it accordinly
class QNAContainer extends StatefulWidget {
final String ques;
final String answer;
QNAContainer({#required this.ques, #required this.answer});
#override
_QNAContainerState createState() => _QNAContainerState();
}
class _QNAContainerState extends State<QNAContainer> {
String truncAns;
bool showingAll = false;
int limit = 80;
#override
void initState() {
super.initState();
if (widget.answer.length > limit ) {
print("truncc");
truncAns = widget.answer.toString().substring(0, limit) + '...';
} else {
truncAns = widget.answer;
}
}
#override
Widget build(BuildContext context) {
ScreenUtil.instance = ScreenUtil(
width: Styles.get_width(context),
height: Styles.get_height(context),
allowFontScaling: true);
return Container(
width: double.infinity,
padding: EdgeInsets.symmetric(horizontal: ScreenUtil().setWidth(10), vertical: ScreenUtil().setHeight(10)),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(5),
color: AppColors.greyFillColor.withOpacity(0.6),
),
margin: EdgeInsets.symmetric(vertical: ScreenUtil().setHeight(7)),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(widget.ques,
style: TextStyle(
fontSize: ScreenUtil().setHeight(14),
fontWeight: FontWeight.bold,
)),
SizedBox(height: ScreenUtil().setHeight(5)),
Text(showingAll ? widget.answer : truncAns,
style: TextStyle(
fontSize: ScreenUtil().setHeight(14),
)),
SizedBox(height: ScreenUtil().setHeight(5)),
truncAns.contains('...')
? GestureDetector(
onTap: () {
setState(() {
showingAll = !showingAll;
});
},
child: Align(
alignment: Alignment.centerRight,
child: Container(
margin: EdgeInsets.only(bottom: ScreenUtil().setHeight(5)),
padding: EdgeInsets.symmetric(vertical: ScreenUtil().setHeight(5), horizontal: ScreenUtil().setWidth(9)),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
color: AppColors.kDefaultPink),
child: Text(
showingAll ? 'see less' : 'see more',
style: TextStyle(color: Colors.white, fontSize: ScreenUtil().setHeight(14)),
),
),
),
)
: SizedBox()
],
),
);
}
}

Navigate Flat Button to Another Screen from Bottom Navigation Bar Item current Index using Provider

I created a provider using the provider package that tells if an item on the BottomNavigationBar is pressed, so that the page displayed matches the item from the BottomNavigationBar on the body properties of the Scaffold. I've made a screen with TextFormField and FlatButton on the first BottomNavigationBar item. what I want to do is, I want to add all the data that has been entered in TextFromField to the third screen item from the BottomNavigationBar that I have created, and then display the third item page from the BottomNavigationBar that was added to the data through FlatButton on first screen.
I have been searching for solutions to this problem for days, but I also haven't found the answer.
My provider for BottomNavigationBar
import 'package:flutter/material.dart';
class Index with ChangeNotifier {
int _currentindex = 0;
get currentindex => _currentindex;
set currentindex(int index){
_currentindex = index;
notifyListeners();
}
}
My Scaffold
import 'package:flutter/material.dart';
import 'package:kakeiboo/View/balance_screen.dart';
import 'package:kakeiboo/View/bignote_screen.dart';
import 'package:kakeiboo/View/daily_screen.dart';
import 'package:provider/provider.dart';
import 'package:kakeiboo/controller/notifier.dart';
import 'package:kakeiboo/constant.dart';
class BottomNavigate extends StatefulWidget {
#override
_BottomNavigateState createState() => _BottomNavigateState();
}
class _BottomNavigateState extends State<BottomNavigate> {
var currentTab = [
BigNotePage(),
DailyExpensesPage(),
BalancePage(),
];
#override
Widget build(BuildContext context) {
var provider = Provider.of<Index>(context);
return Scaffold(
resizeToAvoidBottomInset: false,
body: currentTab[provider.currentindex],
bottomNavigationBar: BottomNavigationBar(
onTap: (index) {
provider.currentindex = index;
},
currentIndex: provider.currentindex,
backgroundColor: Color(0xff2196f3),
showUnselectedLabels: false,
selectedItemColor: Color(0xffffffff),
type: BottomNavigationBarType.fixed,
items: [
BottomNavigationBarItem(
icon: Icon(Icons.book),
title: Text(
'Big Note',
style: kBottomNavigateStyle,
),
),
BottomNavigationBarItem(
icon: Icon(Icons.receipt),
title: Text(
'Daily',
style: kBottomNavigateStyle,
),
),
BottomNavigationBarItem(
icon: Icon(Icons.account_balance_wallet),
title: Text(
'Balance',
style: kBottomNavigateStyle,
),
),
],
),
);
}
}
My First Screen
import 'package:flutter/material.dart';
import 'package:kakeiboo/View/balance_screen.dart';
import 'package:kakeiboo/constant.dart';
class BigNotePage extends StatefulWidget {
#override
_BigNotePageState createState() => _BigNotePageState();
}
class _BigNotePageState extends State<BigNotePage> {
bool _validate = false;
final _formKey = GlobalKey<FormState>();
final _incomeController = TextEditingController();
final _expensesController = TextEditingController();
final _savingsController = TextEditingController();
#override
void dispose() {
_incomeController.dispose();
_expensesController.dispose();
_savingsController.dispose();
super.dispose();
}
void cek() {
String income = _incomeController.text;
String expenses = _expensesController.text;
String savings = _savingsController.text;
if (int.parse(income) >= int.parse(expenses) + int.parse(savings)) {
_formKey.currentState.save();
Navigator.push(context, MaterialPageRoute(builder: (context)=>BalancePage()));
} else {
setState(() {
_validate = true;
});
}
}
#override
Widget build(BuildContext context) {
return Container(
padding: kPading,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
TitlePage('Big Note'),
Expanded(
child: Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisAlignment: MainAxisAlignment.center,
children: [
TxtField(
controler: _incomeController,
label: 'Income',
),
TxtField(
controler: _expensesController,
label: 'Expenses',
error: _validate
? 'Expenses + Savings Are More Than Income'
: null,
),
TxtField(
controler: _savingsController,
label: 'Savings',
error: _validate
? 'Expenses + Savings Are More Than Income'
: null,
),
Container(
padding: EdgeInsets.symmetric(vertical: 12.0),
child: FlatButton(
padding: EdgeInsets.symmetric(vertical: 14.0),
onPressed: cek,
child: Text(
'WRITE THAT',
style: TextStyle(letterSpacing: 1.25),
),
color: Colors.yellow,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(30.0),
),
),
),
],
),
),
),
Container(
width: 250.0,
child: Text(
'*if you get another income for this mounth, input the income again.',
style: TextStyle(fontSize: 12.0),
),
),
],
),
);
}
}
class TxtField extends StatelessWidget {
TxtField({this.label, this.controler, this.error});
final String label;
final TextEditingController controler;
final String error;
#override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.symmetric(vertical: 12.0),
child: TextFormField(
controller: controler,
keyboardType: TextInputType.numberWithOptions(decimal: true),
decoration: InputDecoration(
errorText: error,
labelText: label,
prefix: Container(
padding: EdgeInsets.all(8.0),
child: Text(
'IDR',
style:
TextStyle(color: Colors.black, fontWeight: FontWeight.bold),
),
),
),
),
);
}
}
My Third Screen
import 'package:flutter/material.dart';
import 'package:kakeiboo/constant.dart';
class BalancePage extends StatefulWidget {
#override
_BalancePageState createState() => _BalancePageState();
}
class _BalancePageState extends State<BalancePage> {
#override
Widget build(BuildContext context) {
return Container(
padding: kPading,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
TitlePage('Balance'),
Expanded(
child: Padding(
padding: EdgeInsets.only(top: 24.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'Savings',
style: TextStyle(fontSize: 24.0),
),
Row(
textBaseline: TextBaseline.alphabetic,
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.baseline,
children: [
Text('IDR'),
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
'5.000.000',
style: TextStyle(
fontSize: 56.0, color: Colors.green),
),
),
],
),
],
),
),
Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10.0),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'Budget',
style: TextStyle(fontSize: 24.0),
),
Row(
textBaseline: TextBaseline.alphabetic,
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.baseline,
children: [
Text('IDR'),
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
'5.000.000',
style: TextStyle(
fontSize: 56.0, color: Colors.red),
),
),
],
),
],
),
),
],
),
),
)
],
),
);
}
}
My First Screen Look
My Third Screen Look
Because you're already using Provider I would suggest using a PageController instead of your class Index(), this is because it will do exactly the same but PageController has some other advantages (as controlling a PageView, and it's already there to avoid more boilerplate when changing page)
//Your Savings model
class MySavings{
int savings = 0;
int income = 0;
int expenses = 0;
}
import 'package:flutter/material.dart';
import 'package:kakeiboo/View/balance_screen.dart';
import 'package:kakeiboo/View/bignote_screen.dart';
import 'package:kakeiboo/View/daily_screen.dart';
import 'package:provider/provider.dart';
import 'package:kakeiboo/controller/notifier.dart';
import 'package:kakeiboo/constant.dart';
//import your Savings model
class BottomNavigate extends StatefulWidget {
#override
_BottomNavigateState createState() => _BottomNavigateState();
}
class _BottomNavigateState extends State<BottomNavigate> {
PageController _pageController;
#override
void initState() {
super.initState();
_pageController = PageController();
}
#override
void dispose(){
super.dispose();
_pageController?.dispose();
}
#override
Widget build(BuildContext context) {
var provider = Provider.of<Index>(context);
return MultiProvider(
providers: [
ChangeNotifierProvider<PageController>.value(value: _pageController), //now the PageController can be seen as any other Provider
Provider<MySavings>(create: (_) => MySavings())
],
child: Scaffold(
resizeToAvoidBottomInset: false,
body: PageView(
controller: _pageController,
physics: NeverScrollableScrollPhysics(), //So the user doesn't scroll and move only when you pressed the buttons
children: <Widget>[
BigNotePage(),
DailyExpensesPage(),
BalancePage(),
],
),
bottomNavigationBar: MyBottomBar()
)
);
}
}
class MyBottomBar extends StatlessWidget{
#override
Widget build(BuildContext context){
final PageController pageController = Provider.of<PageController>(context, listen: false);
final int index = context.select<PageController, int>((pageController) => pageController.hasClients ? pageController.page.round() : pageController.initialPage);// the index the pageController currently is (if there is no client attached it uses the initialPAge that defaults to index 0)
return BottomNavigationBar(
onTap: (index) {
pageController.jumpToPage(index);
//In case you want it animated uncomment the next line and comment jumpToPage()
//pageController.animateToPage(index, duration: const Duration(milliseconds: 300), curve: Curves.ease);
},
currentIndex: index,
backgroundColor: Color(0xff2196f3),
showUnselectedLabels: false,
selectedItemColor: Color(0xffffffff),
type: BottomNavigationBarType.fixed,
items: [
BottomNavigationBarItem(
icon: Icon(Icons.book),
title: Text(
'Big Note',
style: kBottomNavigateStyle,
),
),
BottomNavigationBarItem(
icon: Icon(Icons.receipt),
title: Text(
'Daily',
style: kBottomNavigateStyle,
),
),
BottomNavigationBarItem(
icon: Icon(Icons.account_balance_wallet),
title: Text(
'Balance',
style: kBottomNavigateStyle,
),
),
],
),
);
}
}
Now in SCREEN 1
import 'package:flutter/material.dart';
import 'package:kakeiboo/View/balance_screen.dart';
import 'package:kakeiboo/constant.dart';
//import your Savings model
class BigNotePage extends StatefulWidget {
#override
_BigNotePageState createState() => _BigNotePageState();
}
class _BigNotePageState extends State<BigNotePage> {
bool _validate = false;
final _formKey = GlobalKey<FormState>();
final _incomeController = TextEditingController();
final _expensesController = TextEditingController();
final _savingsController = TextEditingController();
#override
void dispose() {
_incomeController.dispose();
_expensesController.dispose();
_savingsController.dispose();
super.dispose();
}
void cek() {
String income = _incomeController.text;
String expenses = _expensesController.text;
String savings = _savingsController.text;
if (int.parse(income) >= int.parse(expenses) + int.parse(savings)) {
final PageController pageController = Provider.of<PageController>(context, listen: false);
final MySavings mySavings = Provider.of<MySavings>(context, listen: false);
mySavings..income = int.parse(income)..expenses = int.parse(expenses)..savings= int.parse(savings);
_formKey.currentState.save();
pageController.jumpToPage(2); //Index 2 is BalancePage
//In case you want it animated uncomment the next line and comment jumpToPage()
//pageController.animateToPage(2, duration: const Duration(milliseconds: 300), curve: Curves.ease);
} else {
setState(() {
_validate = true;
});
}
}
#override
Widget build(BuildContext context) {
return Container(
padding: kPading,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
TitlePage('Big Note'),
Expanded(
child: Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisAlignment: MainAxisAlignment.center,
children: [
TxtField(
controler: _incomeController,
label: 'Income',
),
TxtField(
controler: _expensesController,
label: 'Expenses',
error: _validate
? 'Expenses + Savings Are More Than Income'
: null,
),
TxtField(
controler: _savingsController,
label: 'Savings',
error: _validate
? 'Expenses + Savings Are More Than Income'
: null,
),
Container(
padding: EdgeInsets.symmetric(vertical: 12.0),
child: FlatButton(
padding: EdgeInsets.symmetric(vertical: 14.0),
onPressed: cek,
child: Text(
'WRITE THAT',
style: TextStyle(letterSpacing: 1.25),
),
color: Colors.yellow,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(30.0),
),
),
),
],
),
),
),
Container(
width: 250.0,
child: Text(
'*if you get another income for this mounth, input the income again.',
style: TextStyle(fontSize: 12.0),
),
),
],
),
);
}
}
class TxtField extends StatelessWidget {
TxtField({this.label, this.controler, this.error});
final String label;
final TextEditingController controler;
final String error;
#override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.symmetric(vertical: 12.0),
child: TextFormField(
controller: controler,
keyboardType: TextInputType.numberWithOptions(decimal: true),
decoration: InputDecoration(
errorText: error,
labelText: label,
prefix: Container(
padding: EdgeInsets.all(8.0),
child: Text(
'IDR',
style:
TextStyle(color: Colors.black, fontWeight: FontWeight.bold),
),
),
),
),
);
}
}
And finally in SCREEEN 3 you just call
final MySavings mysavings = Provider.of<MySavings>(context, listen: false);
and use its values (savings, expenses and income) to display in each Text, do some math if you want or change its value (If you want to update as soon as you change them then make MySavings a ChangeNotifier).
If you dont't want to use PageView and stick with the Index class just check the logic and change all the PageController with your index provider and it should work the same
You could save the TextFormField value to a provider property, this way it will be available on all screens