Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 2 years ago.
Improve this question
I'm new with Flutter and need some help. I used to build apps with Phonegap, but they've stoped delivering their services.
I'm facing a problem with the following: I'm trying to create a container with a button in it. When pushing the button the container must fill (animate) the screen in 1 second.
So it has to be filled untill the bottom bar, it must not go over the bottom.
See example: the container (orange) which is under the other container (green), is a list view.
I've tried using several widgets (Column, ListView, Container) yet no result. Could someone help me out with this? Which widget should I use? Any advise is welcome.
example
You can achieve given example like this:
class MyHomePage extends StatefulWidget {
MyHomePage({Key key}) : super(key: key);
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage>
with SingleTickerProviderStateMixin {
double unexpandedTileHeight = 200.0;
bool isExpandedMode = false;
int _bottomNavIndex = 0;
final double initialUnexpandedTileHeight = 200.0;
final double roundedButtonSize = 48.0;
final double bottomBarSize = 64.0;
AnimationController _animationController;
#override
void initState() {
super.initState();
_animationController =
AnimationController(vsync: this, duration: Duration(milliseconds: 450));
}
#override
void dispose() {
_animationController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Stack(
fit: StackFit.expand,
children: [
MainPage(),
AnimatedPositioned(
duration: Duration(milliseconds: 200),
top: unexpandedTileHeight,
left: 0,
right: 0,
bottom: 0,
child: OverlayPage(),
),
AnimatedPositioned(
duration: Duration(milliseconds: 200),
left: MediaQuery.of(context).size.width / 2 -
roundedButtonSize / 2,
top: unexpandedTileHeight -
(isExpandedMode
? roundedButtonSize + 4
: roundedButtonSize / 2),
child: RawMaterialButton(
fillColor: Colors.white,
constraints: BoxConstraints.tightFor(
width: roundedButtonSize, height: roundedButtonSize),
onPressed: () {
isExpandedMode
? setState(() {
isExpandedMode = false;
unexpandedTileHeight = initialUnexpandedTileHeight;
_animationController.reverse();
})
: setState(() {
isExpandedMode = true;
var mediaQueryData = MediaQuery.of(context);
unexpandedTileHeight = mediaQueryData.size.height -
bottomBarSize -
mediaQueryData.padding.top -
mediaQueryData.padding.bottom;
_animationController.forward();
});
},
shape: StadiumBorder(),
child: AnimatedIcon(
icon: AnimatedIcons.list_view,
progress: _animationController,
),
))
],
),
),
bottomNavigationBar: SizedBox(
height: bottomBarSize,
child: BottomNavigationBar(
backgroundColor: Colors.white,
currentIndex: _bottomNavIndex,
onTap: (index) => setState(() {
_bottomNavIndex = index;
}),
type: BottomNavigationBarType.fixed,
items: [
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: 'Home',
),
BottomNavigationBarItem(
icon: Icon(Icons.radio),
label: 'Radio',
),
],
),
),
);
}
}
class OverlayPage extends StatelessWidget {
const OverlayPage({
Key key,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return Container(
color: Colors.orange,
);
}
}
class MainPage extends StatelessWidget {
const MainPage({
Key key,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.symmetric(
horizontal: 16.0,
),
color: Colors.green,
child: Column(
children: [
SizedBox(height: 16),
Text('bla bla bla'),
SizedBox(height: 24),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.photo,
size: 56,
),
SizedBox(width: 24),
Text(
'bla bla bla',
style: TextStyle(fontSize: 18),
),
],
),
SizedBox(height: 100),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'dasdadasds',
style: TextStyle(fontSize: 18),
),
Text(
'fdsfsdf',
style: TextStyle(fontSize: 18),
),
],
),
SizedBox(height: 16),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'dasdadasds',
style: TextStyle(fontSize: 18),
),
Text(
'fdsfsdf',
style: TextStyle(fontSize: 18),
),
],
),
SizedBox(height: 16),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'dasdadasds',
style: TextStyle(fontSize: 18),
),
Text(
'fdsfsdf',
style: TextStyle(fontSize: 18),
),
],
),
SizedBox(height: 16),
],
),
);
}
}
Related
I'm trying to flip a card from front to back and back to front without using the dependencies, but could not implement. I have seen where people have used a pre defined Flip Card package but without the dependency I'm finding trouble. Please help me out.
I want the card to flip to back as soon as I click on the "Icon Button" and back as soon as I click "Go Back" Button. I tried the idea without using the animation and is working just fine, but how do I implement the flip animation is what I feel is difficult.
class NotificationItemCard extends StatefulWidget {
const NotificationItemCard({
Key? key,
}) : super(key: key);
#override
State<NotificationItemCard> createState() => _NotificationItemCardState();
}
class _NotificationItemCardState extends State<NotificationItemCard> {
late bool showCardFrontSide;
#override
void initState() {
showCardFrontSide = true;
super.initState();
}
void onChangeView() {
setState(() {
showCardFrontSide = !showCardFrontSide;
});
}
#override
Widget build(BuildContext context) {
return Stack(
children: [
Container(
height: 140.h,
decoration: BoxDecoration(
border: Border.all(color: Colors.black),
borderRadius: BorderRadius.circular(8),
),
child: showCardFrontSide
? const NotificationCardFrontSide()
: NotificationCardBackSide(
onChangeView: onChangeView,
),
),
showCardFrontSide
? Align(
alignment: const Alignment(0.95, -1),
child: IconButton(
key: const ValueKey("IconButton"),
onPressed: onChangeView,
icon: const Icon(Icons.info_outline),
),
)
: const SizedBox.shrink()
],
);
}
}
class NotificationCardFrontSide extends StatelessWidget {
const NotificationCardFrontSide({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Row(
children: [
SizedBox(
key: const ValueKey("FrontSideSizedBox"),
width: 126.w,
child: Center(
child: CircleAvatar(
radius: 50.r,
),
),
),
SizedBox(
key: const ValueKey("FrontSideSizedTextBox"),
width: 222.w,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
"Premium Private LOBBY",
style: Theme.of(context).textTheme.headlineMedium?.copyWith(overflow: TextOverflow.ellipsis),
key: const ValueKey("FrontSideSizedTextBox1"),
),
Text(
"Prediction Deadline",
// "Prediction Deadline - ${DateConverterUtil.convert(lobby.match.start)}",
style: Theme.of(context).textTheme.headlineMedium?.copyWith(overflow: TextOverflow.ellipsis),
key: const ValueKey("FrontSideSizedTextBox2"),
),
Text(
"Premium Private LOBBY",
style: Theme.of(context).textTheme.headlineMedium?.copyWith(overflow: TextOverflow.ellipsis),
key: const ValueKey("FrontSideSizedTextBox3"),
),
SizedBox(
key: const ValueKey("FrontSideSizedButtonBox"),
width: 150.w,
height: 45.h,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
SizedBox(
key: const ValueKey("FrontSideButtonSizedBox"),
width: 70.w,
child: TextButton(
onPressed: () {},
child: Text(
"deny",
style: Theme.of(context).textTheme.bodyMedium,
),
),
),
SizedBox(
width: 70.w,
child: TextButton(
onPressed: () {},
child: Text(
"deny",
style: Theme.of(context).textTheme.bodyMedium,
),
),
),
],
),
),
],
),
),
],
);
}
}
class NotificationCardBackSide extends StatelessWidget {
final VoidCallback onChangeView;
const NotificationCardBackSide({
Key? key,
required this.onChangeView,
}) : super(key: key);
Widget getTeamLogo(String image) {
return CircleAvatar(
backgroundColor: const Color(0xFFD9D9D9),
radius: 30.r,
child: Image.network(
image,
errorBuilder: (context, error, stackTrace) {
return Text(
"Error",
style: Theme.of(context).textTheme.displayMedium?.copyWith(
color: Colors.red,
),
);
},
height: 65.h,
width: 65.w,
loadingBuilder: (context, child, loadingProgress) {
if (loadingProgress == null) return child;
return Center(
child: Text(
"Loading...",
style: Theme.of(context).textTheme.displayMedium,
),
);
},
),
);
}
#override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
SizedBox(
height: 62.h,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
getTeamLogo(""),
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
"Premium Private LOBBY",
style: Theme.of(context).textTheme.headlineMedium?.copyWith(overflow: TextOverflow.clip),
key: const ValueKey("BackSideSizedText1"),
),
Text(
"Prediction Deadline",
// "Prediction Deadline - ${DateConverterUtil.convert(lobby.match.start)}",
style: Theme.of(context).textTheme.headlineMedium?.copyWith(overflow: TextOverflow.clip),
key: const ValueKey("BackSideSizedText2"),
),
],
),
getTeamLogo(""),
],
),
),
SizedBox(
key: const ValueKey("BackSideButtonBox"),
height: 30.h,
width: 100.w,
child: OutlinedButton(
onPressed: onChangeView,
child: const Text("Go Back"),
key: const ValueKey("BackSideButtonText"),
style: ButtonStyle(
shape: MaterialStateProperty.all(
RoundedRectangleBorder(
borderRadius: BorderRadius.circular(
8.r,
),
),
),
),
),
)
],
);
}
}
You can implement this with AnimatedBuilder and Transform, Use example below:
import 'dart:math';
import 'package:flutter/material.dart';
void main() {
runApp(MaterialApp(debugShowCheckedModeBanner: false, home: Scaffold(body: MyApp())));
}
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
#override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> with SingleTickerProviderStateMixin {
late AnimationController _controller;
Widget _front = Card1();
Widget _back = Card2();
late Widget _card = _front;
#override
void initState() {
super.initState();
_controller = AnimationController(vsync: this, duration: Duration(milliseconds: 600));
_controller.addListener(() {
if (_controller.value >= .5 && _card != _back) {
setState(() => _card = _back);
} else if (_controller.value < .5 && _card != _front) {
setState(() => _card = _front);
}
});
}
#override
void dispose() {
_controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Center(
child: GestureDetector(
onTap: () {
if (_controller.value == 1)
_controller.reverse(from: 1);
else
_controller.forward(from: 0);
},
child: AnimatedBuilder(
animation: _controller,
builder: (c, anim) => Transform(
transform: Matrix4.identity()
..setEntry(3, 2, 0.0025)
..rotateY(_controller.value * pi),
alignment: FractionalOffset.center,
child: _card,
),
),
),
);
}
}
class Card1 extends StatelessWidget {
const Card1({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Container(
width: 150,
height: 300,
color: Colors.red,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('This is Card1'),
Text('I\'m front of the card'),
],
),
);
}
}
class Card2 extends StatelessWidget {
const Card2({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Transform.scale(
scaleX: -1,
child: Container(
width: 150,
height: 300,
color: Colors.blue,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('This is Card2'),
Text('I\'m back of the card'),
],
),
),
);
}
}
The result:
(Thanks to this answer for transform code)
I am building an online bottle store app using flutter and I am having an issue where if I add a product to favorites the selected product's button won't stay selected on the home page if I switch pages. I have categorized the products using a Tabbar and Tabbarview. I have tried using AutomaticKeepAliveClientMxin to keep the page alive but with no success. Please can anyone assist.
Here's what happens:
I click on the selected product
then it is added to Favorites
Come back to the home page and the selected item is no longer showing that it is selected
Here's my code:
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
#override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage>
with AutomaticKeepAliveClientMixin, TickerProviderStateMixin {
ProductProvider productProvider = ProductProvider();
late TabController tabController;
#override
void initState() {
super.initState();
tabController = TabController(length: 4, vsync: this);
}
#override
void dispose() {
tabController.dispose();
super.dispose();
}
#override
bool get wantKeepAlive => true;
#override
Widget build(BuildContext context) {
super.build(context);
var cart = Provider.of<ShoppingCartProvider>(context);
var favoriteProvider = Provider.of<FavoriteProvider>(context);
Size _screenSize = MediaQuery.of(context).size;
final double itemHeight = (_screenSize.height - kToolbarHeight - 24) / 2;
final double itemWidth = _screenSize.width / 2;
return Scaffold(
body: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Padding(
padding: EdgeInsets.all(8.0),
child: Text(
'Categories',
style: TextStyle(
fontSize: 20.0,
fontFamily: 'Montserrat-ExtraBold',
fontWeight: FontWeight.bold),
),
),
Container(
child: Align(
alignment: Alignment.centerLeft,
child: TabBar(
controller: tabController,
indicator:
CircleTabIndicator(color: Colors.redAccent, radius: 4.0),
isScrollable: true,
labelColor: Colors.redAccent,
labelStyle: const TextStyle(
fontWeight: FontWeight.bold, fontSize: 20.0),
unselectedLabelColor: Colors.black,
unselectedLabelStyle: const TextStyle(
fontWeight: FontWeight.bold, fontSize: 20.0),
tabs: const [
Tab(text: 'Brandy'),
Tab(text: 'Gin'),
Tab(text: 'Soft drinks'),
Tab(text: 'Whiskey')
],
),
),
),
Container(
height: 400,
width: double.maxFinite,
child: TabBarView(
controller: tabController,
children: productProvider.categories.map((bottleCategory) {
return GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
childAspectRatio: itemWidth / itemHeight,
),
itemCount: bottleCategory.bottleList.length,
itemBuilder: (context, index) {
return Card(
shadowColor: Colors.grey,
surfaceTintColor: Colors.amber,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20)),
child: Stack(
children: [
Positioned(
right: 0,
child: InkWell(
onTap: () {
favoriteProvider.toggleFavorites(
bottleCategory.bottleList[index]);
if (favoriteProvider.isExist(
bottleCategory.bottleList[index])) {
ScaffoldMessenger.of(context)
.hideCurrentSnackBar();
ScaffoldMessenger.of(context)
.showSnackBar(
const SnackBar(
content: Text(
"Product Added to Favorite!",
style: TextStyle(fontSize: 16),
),
backgroundColor: Colors.green,
duration: Duration(seconds: 1),
),
);
} else {
ScaffoldMessenger.of(context)
.hideCurrentSnackBar();
ScaffoldMessenger.of(context)
.showSnackBar(
const SnackBar(
content: Text(
"Product Removed from Favorite!",
style: TextStyle(fontSize: 16),
),
backgroundColor: Colors.red,
duration: Duration(seconds: 1),
),
);
}
},
child: favoriteProvider.isExist(
bottleCategory.bottleList[index])
? const Icon(
Icons.favorite,
color: Colors.redAccent,
)
: const Icon(Icons.favorite_border),
),
),
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Center(
child: Image.asset(
bottleCategory.bottleList[index].image,
height: 200.0,
),
),
Center(
child: Text(
bottleCategory
.bottleList[index].bottleName,
style: const TextStyle(
fontSize: 20.0,
fontWeight: FontWeight.bold))),
Center(
child: Text(
'R${bottleCategory.bottleList[index].price}'),
)
],
),
Positioned(
bottom: 0,
right: 10,
child: IconButton(
icon: const Icon(Icons.add_circle),
iconSize: 40.0,
onPressed: () {
cart.addToCart(
bottleCategory.bottleList[index].id,
bottleCategory
.bottleList[index].bottleName,
bottleCategory
.bottleList[index].price,
bottleCategory
.bottleList[index].image);
},
))
],
),
);
},
);
}).toList()),
),
],
),
),
);
}
}
class FavoriteProvider with ChangeNotifier {
List<Bottle> _favItems = [];
List<Bottle> get favItems {
return [..._favItems];
}
void toggleFavorites(Bottle favBottle) {
final isExist = _favItems.contains(favBottle);
if (isExist) {
_favItems.remove(favBottle);
} else {
_favItems.add(favBottle);
}
notifyListeners();
}
bool isExist(Bottle favBottle) {
final isExist = _favItems.contains(favBottle);
return isExist;
}
void clearFavorite() {
_favItems = [];
notifyListeners();
}
}
Try using Consumer widget. Like so:
Consumer<favoriteProvider>(
builder: (BuildContext context, favorite, _){
return Icon(
Icons.favorite,
color: favorite.isExist(bottleCategory.bottleList[index])? Colors.redAccent : null,
);
},
),
Consumer widget will refresh or change the state whenever the ChangeNotifier of that model, in this case, FavoriteProvider is triggered, this should allows your widget to change and check itself anytime. So you shouldn't need to keep your state or screen alive all the time.
If that doesn't work, please change your Business Logic in the FavoriteProvider. Instead of using contains, I suggest to use any and identifies each instances with its own id or any of its unique variable. Like so:
bool isExist(Bottle favBottle) {
final isExist = _favItems.any((e) =>e.bottleName == favBottle.bottleName);
return isExist;
}
I am currently working on an application and I have a warning that I can't get rid of...
Summary: A RenderFlex overflowed by 34 pixels on the right -> When you
press the button to expand container's width using an AnimatedController.
I use a custom floatingActionButton in a Scaffold which when pressed extends to the end of the screen and shows 2 options. The position of the button is in the lower right and I want that when you press it once it extends to the left and two rows of buttons appear.
When you press the button, the warrning specified above appears and disappears. I tried with Expanded but I didn't succeed ...
I attach the code:
import 'package:flutter/material.dart';
class AnimatedContainerButton extends StatefulWidget {
const AnimatedContainerButton({Key? key}) : super(key: key);
#override
AnimatedContainerButtonState createState() => AnimatedContainerButtonState();
}
class AnimatedContainerButtonState extends State<AnimatedContainerButton> {
bool _isExpanded = false;
late double _screenWidth;
void closeMenu() {
setState(() {
_isExpanded = false;
});
}
void openMenu() {
setState(() {
_isExpanded = true;
});
}
#override
Widget build(BuildContext context) {
_screenWidth = MediaQuery.of(context).size.width;
return AnimatedContainer(
curve: Curves.easeInBack,
duration: Duration(milliseconds: 500),
height: 50,
width: _isExpanded ? _screenWidth - 35 : 50,
decoration: BoxDecoration(
color: Colors.purple,
borderRadius: BorderRadius.all(
Radius.circular(50),
),
),
child: Expanded(
child: _isExpanded
? Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
InkWell(
child: Column(
children: <Widget>[
Icon(Icons.flutter_dash_rounded),
Text(
"Item1",
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
),
),
],
),
),
InkWell(
child: Column(
children: <Widget>[
Icon(Icons.flutter_dash_rounded),
Text(
"Item2",
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
),
),
],
),
),
],
)
: IconButton(
color: Colors.white,
icon: Icon(Icons.add),
onPressed: () {
openMenu();
},
),
),
);
}
}
I am using AnimationController.animateTo method to jump my animation to specific points instantly.
When I pass parameter 'target' with a value of 0.0 to animateTo method, the resulting 'value' property of AnimationController is 0.0. If I pass a parameter 'target' to animateTo method with a value of 0.5, the resulting 'value' property of AnimationController still returns 0.0. I would expect the 'value' property to return 0.5.
void _onPlayButtonPressed() {
final cpm = ScopedModel.of<CpModel>(context);
if (cpm.started) {
cpm.stop();
_animController.stop();
var targetProgress = cpm.adjustedProgress;
_animController.animateTo(targetProgress , duration: Duration(seconds: 0));
} else {
_ranOnce = true;
cpm.start();
_animController.forward();
}
}
I got late to answer that but will be helpful for other developers.
Add your animation controller onto the Animation object and use that animation object.
Like this:
The following example shows how you can use AnimationController's toAnimate method:
class Example extends StatefulWidget {
const Example({Key? key}) : super(key: key);
#override[![demo][1]][1]
State<Example> createState() => _ExampleState();
}
class _ExampleState extends State<Example> with TickerProviderStateMixin {
late Animation<double> slideAnimation;
late AnimationController sliderAnimationController;
#override
void initState() {
sliderAnimationController = AnimationController(
vsync: this, duration: const Duration(milliseconds: 1000));
slideAnimation = Tween<double>(begin:0, end: 130).animate(CurvedAnimation(
parent: sliderAnimationController, curve: Curves.ease));
print( sliderAnimationController.value);
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(body: SizedBox(width: 130,height: 160,child: IntrinsicHeight(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 10),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
InkWell(
onTap: () {
sliderAnimationController.animateTo(0.0);
},
child: Padding(
padding: const EdgeInsets.all(6.0),
child: Text(
"Text1",
style: Theme.of(context)
.textTheme
.bodyText1!
.copyWith(
color: Colors.black,
fontWeight: FontWeight.w600),
),
),
),
12.verticalSpace,
InkWell(
onTap: () {
// sliderAnimationController.forward();
sliderAnimationController.animateTo(0.215);
},
child: Padding(
padding: const EdgeInsets.all(6.0),
child: Text(
"Text2",
style: Theme.of(context)
.textTheme
.subtitle1!
.copyWith(color: Colors.black),
),
),
),
12.verticalSpace,
InkWell(
onTap: () {
sliderAnimationController.animateTo(0.405);
}
,
child: Padding(
padding: const EdgeInsets.all(6.0),
child: Text(
"Text3",
style: Theme.of(context)
.textTheme
.subtitle1!
.copyWith(color: Colors.black),
),
),
),
12.verticalSpace,
InkWell(
onTap: () {
sliderAnimationController.animateTo(1.0);
},
child: Padding(
padding: const EdgeInsets.all(6.0),
child: Text(
"Text4",
style: Theme.of(context)
.textTheme
.subtitle1!
.copyWith(color: Colors.black),
),
),
)
],
),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 10),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
AnimatedBuilder(
animation: sliderAnimationController,
builder: (BuildContext context, Widget? child) {
return Transform.translate(offset: Offset(0, slideAnimation.value),
child: child);
},
child: Container(
width: 3,
height: 26,
color: Colors.blue,
),
),
Container(
width: 1,
color: Colors.black54,
)
],
),
),
],
),
),
),));
}
#override
void dispose() {
sliderAnimationController.dispose();
super.dispose();
}
}
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