Flutter Linear progress indicator for each page of pageview builder - flutter

I want to build a progress indicator for each page of the pageview builder. Pageview changes after 10 seconds and the progress bar shifts to the next bar. I have successfully implemented this, but encounter an issue of 4 bars loading at the same time, while going back and forth to the page view screen or when I minimize and reopen the app.
This is what I already have achieved with the following code:
This is my code:
class WelcomeScreen extends StatefulWidget {
const WelcomeScreen({Key? key}) : super(key: key);
#override
State<WelcomeScreen> createState() => _WelcomeScreenState();
}
class _WelcomeScreenState extends State<WelcomeScreen>
with TickerProviderStateMixin {
final WelcomeController controller = Get.put(WelcomeController());
late AnimationController animationControllerOne;
late AnimationController animationControllerTwo;
late AnimationController animationControllerThree;
late AnimationController animationControllerFour;
late Animation<double> animation;
#override
void initState() {
super.initState();
animationControllerOne =
AnimationController(duration: const Duration(seconds: 10), vsync: this);
animation = Tween(begin: 0.0, end: 1.0).animate(animationControllerOne)
..addListener(() {
setState(() {
});
});
animationControllerOne.forward();
animationControllerTwo =
AnimationController(duration: const Duration(seconds: 10), vsync: this);
animation = Tween(begin: 1.0, end: 2.0).animate(animationControllerTwo)
..addListener(() {
setState(() {
});
});
Timer(const Duration(seconds: 10), () {
animationControllerTwo.forward();
});
animationControllerThree =
AnimationController(duration: const Duration(seconds: 10), vsync: this);
animation = Tween(begin: 2.0, end: 3.0).animate(animationControllerThree)
..addListener(() {
setState(() {});
});
Timer(const Duration(seconds: 20), () {
animationControllerThree.forward();
});
animationControllerFour =
AnimationController(duration: const Duration(seconds: 10), vsync: this);
animation = Tween(begin: 3.0, end: 4.0).animate(animationControllerFour)
..addListener(() {
setState(() {});
});
Timer(const Duration(seconds: 30), () {
animationControllerFour.forward();
});
animationControllerFour.addStatusListener((status) {
if (status == AnimationStatus.completed) {
animationControllerOne.reset();
animationControllerTwo.reset();
animationControllerThree.reset();
animationControllerFour.reset();
animationControllerOne.forward();
}
animationControllerOne.addStatusListener((status) {
if (status == AnimationStatus.completed) {
animationControllerTwo.reset();
animationControllerTwo.forward();
}
});
animationControllerTwo.addStatusListener((status) {
if (status == AnimationStatus.completed) {
animationControllerThree.reset();
animationControllerThree.forward();
}
});
animationControllerThree.addStatusListener((status) {
if (status == AnimationStatus.completed) {
animationControllerFour.reset();
animationControllerFour.forward();
}
});
});
}
#override
void dispose() {
animationControllerOne.dispose();
animationControllerTwo.dispose();
animationControllerThree.dispose();
animationControllerFour.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: context.theme.scaffoldBackgroundColor,
appBar: AppBar(
systemOverlayStyle: SystemUiOverlayStyle(
statusBarColor: Get.isDarkMode ? darkBackground : lightBackground,
statusBarIconBrightness:
Get.isDarkMode ? Brightness.light : Brightness.dark,
),
title: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Row(
key: const Key('loading bar'),
children: [
Expanded(
child: LinearProgressIndicator(
color: primaryBlue,
backgroundColor: const Color(0xFFD9D9D9),
minHeight: 8,
value: animationControllerOne.value,
),
),
const SizedBox(
width: 10,
),
Expanded(
child: LinearProgressIndicator(
color: primaryBlue,
backgroundColor: const Color(0xFFD9D9D9),
minHeight: 8,
value: animationControllerTwo.value,
),
),
const SizedBox(
width: 10,
),
Expanded(
child: LinearProgressIndicator(
color: primaryBlue,
backgroundColor: const Color(0xFFD9D9D9),
minHeight: 8,
value: animationControllerThree.value,
),
),
const SizedBox(
width: 10,
),
Expanded(
child: LinearProgressIndicator(
backgroundColor: const Color(0xFFD9D9D9),
color: primaryBlue,
minHeight: 8,
value: animationControllerFour.value,
),
),
],
),
),
elevation: 0,
backgroundColor: Get.isDarkMode ? darkBackground : lightBackground,
centerTitle: true,
),
body: Padding(
padding: const EdgeInsets.symmetric(vertical: 15),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'Welcome to Demonstration Bank!'.tr,
key: const Key('message'),
textAlign: TextAlign.center,
style: TextStyle(
color: Get.isDarkMode ? lightBackground : darkBackground,
fontSize: Get.height * 0.027,
fontWeight: FontWeight.w600,
),
),
SizedBox(
height: Get.height * 0.05,
),
SizedBox(
height: Get.height * 0.4,
width: Get.width * 0.8,
child: PageView.builder(
controller: controller.gifsController,
physics: const NeverScrollableScrollPhysics(),
itemCount: controller.images.length,
scrollDirection: Axis.horizontal,
itemBuilder: (context, index) {
return Image.asset(
controller.images[index],
fit: BoxFit.fitWidth,
);
},
),
),
SizedBox(
height: Get.height * 0.15,
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
CustomButton(
key: const Key('sign in button'),
height: Get.height * 0.069,
width: Get.width * 0.335,
fontSize: Get.height * 0.021,
color: Get.isDarkMode ? lightBackground : darkBackground,
textColor: Get.isDarkMode ? darkBackground : lightBackground,
text: 'Sign in'.tr,
onPressed: () => Get.to(() => const SigninScreen()),
),
const SizedBox(
width: 19,
),
CustomButton(
key: const Key('sign up button'),
height: Get.height * 0.069,
width: Get.width * 0.335,
fontSize: Get.height * 0.021,
color: primaryBlue,
textColor: Colors.white,
text: 'Sign Up'.tr,
onPressed: () => Get.to(() => CreateAccountScreen()),
),
],
),
const SizedBox(
height: 24,
),
],
),
),
);
}
}
Unexpected result: ( Note: this problem does not happen always )

Related

How to implement GetX and Obx to a tab bar?

I'm making a Pomodoro app and I don't know how to implement Get and Obx if the timer is over and make that the tab bar change automatically.
This is my code:
Tab bar:
#override
Widget build(BuildContext context) {
return SizedBox(
height: MediaQuery.of(context).size.height,
width: double.infinity,
child: AnimatedBuilder(
animation: _countDownController.controller,
builder: (context, child) {
return DefaultTabController(
length: 3,
child: Scaffold(
appBar: AppBar(
elevation: 0,
backgroundColor: Colors.transparent,
bottom: PreferredSize(
preferredSize: const Size.fromHeight(22),
child: Container(
color: Colors.transparent,
child: SafeArea(
child: ResponsiveWeb(
child: Column(children: [
TabBar(
controller: _tabController,
indicator: const UnderlineTabIndicator(
borderSide: BorderSide(
color: Color(0xff3B3B3B), width: 2.0),
insets: EdgeInsets.fromLTRB(
12.0, 12.0, 12.0, 12.0)),
indicatorWeight: 5,
indicatorSize: TabBarIndicatorSize.label,
labelColor: const Color(0xff3B3B3B),
labelStyle: GoogleFonts.nunito(
fontSize: 16.0,
// letterSpacing: 1,
fontWeight: FontWeight.w500),
unselectedLabelColor: const Color(0xffD7D7D7),
tabs: const [
Tab(
text: "Pomodoro",
icon: Icon(Icons.work_history_outlined,
size: 24),
),
Tab(
text: "Short break",
icon: Icon(Icons.ramen_dining_outlined,
size: 24),
),
Tab(
text: "Long break",
icon: Icon(
Icons.battery_charging_full_outlined,
size: 24),
),
]),
]),
),
),
),
),
),
),
);
},
),
);
}
}
And my timer:
This is an example from an animation which means that if the timer starts the animation starts as well
createAnimationController(TickerProvider ticker) {
currentRoundType = typeRound.pomodoro;
_changeCurrentRoundTypeString();
tickerProvider = ticker;
currentRoundSeconds.value = currentRoundType == typeRound.pomodoro
? _settingsController.secondsWork.value
: currentRoundNumber.value < _settingsController.rounds.value
? _settingsController.secondsBreak.value
: _settingsController.secondsBreakAfterRound.value;
restartTimers();
controller = AnimationController(
vsync: tickerProvider,
duration: currentDuration,
);
logger.d(controller.value);
painter = CustomTimePainter(
backgroundColor: const Color.fromARGB(0, 33, 149, 243),
color: const Color(0xffD94530),
animation: controller,
);
timerString.value =
'${(currentDuration.inHours).toString().padLeft(2, '0')}:${(currentDuration.inMinutes % 60).toString().padLeft(2, '0')}:${(currentDuration.inSeconds % 60).toString().padLeft(2, '0')}';
listRounds.value = List.generate(
_settingsController.rounds.value + 1, (index) => Rx(stateRound.undone));
}
This is another example, which means that if the timer ends the empty image is colored with red color
import 'package:flutter/material.dart';
import 'package:get/get.dart%20';
import 'package:pomodoro/3.tomatoes_interval_UI/countdown_controller.dart';
import 'package:pomodoro/3.tomatoes_interval_UI/tomato_icon.dart';
class TomatoesIcons extends StatefulWidget {
const TomatoesIcons({super.key});
#override
State<TomatoesIcons> createState() => _TomatoesIconsState();
}
class _TomatoesIconsState extends State<TomatoesIcons>
with TickerProviderStateMixin {
final CountDownController _countDownController = Get.find();
final ScrollController _horizontal = ScrollController();
#override
void initState() {
super.initState();
_countDownController.createAnimationController(this);
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: AnimatedBuilder(
animation: _countDownController.controller,
builder: (context, child) {
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
color: const Color.fromARGB(255, 255, 202, 55),
height: 65,
width: MediaQuery.of(context).size.width,
child: Center(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Obx(
() => Scrollbar(
controller: _horizontal,
child: SingleChildScrollView(
controller: _horizontal,
scrollDirection: Axis.horizontal,
child: Row(
children: _countDownController.listRounds
.map(
(e) => MouseRegion(
cursor: SystemMouseCursors.click,
child: TomatoIcon(e),
),
)
.toList(),
),
),
),
),
),
),
),
],
);
},
),
),
);
}
}
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:pomodoro/3.tomatoes_interval_UI/countdown_controller.dart';
class TomatoIcon extends StatefulWidget {
final Rx<stateRound> state;
const TomatoIcon(this.state, {Key? key}) : super(key: key);
#override
State<TomatoIcon> createState() => _TomatoIconState();
}
class _TomatoIconState extends State<TomatoIcon> {
#override
Widget build(BuildContext context) {
return Obx(
() => IconButton(
onPressed: null,
icon: widget.state.value == stateRound.done
? Image.asset('assets/icons/tomatoDone.png')
: Image.asset('assets/icons/tomatoUndone.png')),
);
}
}
With these examples, I would like to create a function or " if statement" which triggers the timer is over, select automatically a tab bar.
Thanks for any help you can provide

How can I maintain the height of an animation widget even if it's contained in another widget?

This is what I mean:
As you can see the animation starts from the top to bottom, the problem begins when I integrate another file into it
Example:
Here, I wrapped with another widget and don't respect the height of the app bar
This is my code:
home_page_timer.dart
import 'dart:core';
import 'package:flutter/material.dart';
import 'package:google_nav_bar/google_nav_bar.dart';
import 'package:pomodoro/5.hourglass_animation/countdown_timer/pomodoro_animation.dart';
import 'dart:async';
class HomePageTimerUI extends StatefulWidget {
const HomePageTimerUI({Key? key}) : super(key: key);
#override
State<HomePageTimerUI> createState() => _HomePageTimerUIState();
}
class _HomePageTimerUIState extends State<HomePageTimerUI>
with TickerProviderStateMixin {
late TabController _tabController;
late Timer timer;
late AnimationController controller;
String get countText {
Duration count = controller.duration! * controller.value;
return controller.isDismissed
? '${controller.duration!.inHours.toString().padLeft(2, '0')}:${(controller.duration!.inMinutes % 60).toString().padLeft(2, '0')}:${(controller.duration!.inSeconds % 60).toString().padLeft(2, '0')}'
: '${count.inHours.toString().padLeft(2, '0')}:${(count.inMinutes % 60).toString().padLeft(2, '0')}:${(count.inSeconds % 60).toString().padLeft(2, '0')}';
}
#override
void initState() {
super.initState();
_tabController = TabController(length: 3, vsync: this);
}
#override
void dispose() {
_tabController.dispose();
super.dispose();
}
void notify() {
if (countText == '00:00:00') {
_tabController.animateTo(1, duration: const Duration(milliseconds: 300));
}
}
#override
Widget build(BuildContext context) {
return Container(
height: MediaQuery.of(context).size.height,
child: DefaultTabController(
length: 3,
child: Scaffold(
appBar: AppBar(
elevation: 0,
backgroundColor: Colors.transparent,
bottom: PreferredSize(
preferredSize: const Size.fromHeight(35),
child: Container(
color: Colors.transparent,
child: SafeArea(
child: Column(
children: <Widget>[
TabBar(
controller: _tabController,
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: 35),
),
Tab(
text: "SHORT BREAK",
icon: Icon(Icons.ramen_dining, size: 35),
),
Tab(
text: "LONG BREAK",
icon: Icon(Icons.battery_charging_full_rounded,
size: 35),
),
])
],
),
),
),
),
),
body: TabBarView(
controller: _tabController,
children: <Widget>[
Center(
child: Container(
height: MediaQuery.of(context).size.height,
child: StartPomodoro(end: DateTime.now())),
),
const Center(
child: Text('short break'),
),
const Center(
child: Text('long break '),
),
],
),
),
),
);
}
}
start_pomodoro.dart
import 'dart:async';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:pomodoro/5.hourglass_animation/countdown_timer/responsive.dart';
class StartPomodoro extends StatefulWidget {
const StartPomodoro({super.key, required this.end});
final DateTime end;
#override
State<StartPomodoro> createState() => _StartPomodoroState();
}
class _StartPomodoroState extends State<StartPomodoro>
with TickerProviderStateMixin {
final now = DateTime.now();
List<bool> isSelected = [true, false];
late Timer timer;
late AnimationController controller;
String get countText {
Duration count = controller.duration! * controller.value;
return controller.isDismissed
? '${controller.duration!.inHours.toString().padLeft(2, '0')}:${(controller.duration!.inMinutes % 60).toString().padLeft(2, '0')}:${(controller.duration!.inSeconds % 60).toString().padLeft(2, '0')}'
: '${count.inHours.toString().padLeft(2, '0')}:${(count.inMinutes % 60).toString().padLeft(2, '0')}:${(count.inSeconds % 60).toString().padLeft(2, '0')}';
}
double progress = 1.0;
bool LongBreak = true;
void notify() {
if (countText == '00:00:00') {}
}
#override
void initState() {
super.initState();
controller = AnimationController(
vsync: this,
duration: const Duration(seconds: 0),
);
controller.addListener(() {
notify();
if (controller.isAnimating) {
setState(() {
progress = controller.value;
});
} else {
setState(() {
progress = 1.0;
LongBreak = true;
});
}
});
}
#override
void dispose() {
controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
backgroundColor:
LongBreak ? const Color(0xffD94530) : const Color(0xff6351c5),
body: Stack(
children: [
GestureDetector(
onTap: () {
if (controller.isDismissed) {
showModalBottomSheet(
context: context,
builder: (context) => SizedBox(
height: 300,
child: CupertinoTimerPicker(
initialTimerDuration: controller.duration!,
onTimerDurationChanged: (time) {
setState(() {
controller.duration = time;
});
},
),
),
);
}
},
child: AnimatedBuilder(
animation: controller,
builder: (context, child) {
return Stack(
children: <Widget>[
Align(
alignment: Alignment.bottomCenter,
child: Container(
color: const Color(0xffD94530),
height: controller.value *
MediaQuery.of(context).size.height,
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Responsive(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Align(
alignment: Alignment.bottomCenter,
child: Align(
alignment: FractionalOffset.bottomCenter,
child: Container(
width:
MediaQuery.of(context).size.width,
height: 210,
decoration: const BoxDecoration(
color: Color.fromARGB(
255, 245, 245, 245),
),
child: Container(
padding: const EdgeInsets.all(20.0),
child: Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
const Text(
"Hyper-focused on... (+add task)",
style: TextStyle(
fontSize: 22.0,
fontWeight: FontWeight.w500,
),
),
const SizedBox(height: 16),
Center(
child: Column(
mainAxisAlignment:
MainAxisAlignment.center,
children: [
Row(
mainAxisAlignment:
MainAxisAlignment
.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Center(
child: Text(
countText,
style:
const TextStyle(
fontWeight:
FontWeight.w600,
letterSpacing: 4,
fontSize: 65.0,
color: Color(
0xff3B3B3B),
),
),
),
],
),
Row(
mainAxisAlignment:
MainAxisAlignment
.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: const [
Center(
child: Text(
' Hours Minutes Seconds ',
style: TextStyle(
fontWeight:
FontWeight.w500,
letterSpacing: 2,
fontSize: 20.0,
color: Color(
0xff3B3B3B),
),
),
),
],
),
],
),
),
],
),
),
),
),
),
//Spacer(),
Responsive(
child: Column(
mainAxisAlignment:
MainAxisAlignment.center,
crossAxisAlignment:
CrossAxisAlignment.stretch,
children: [
AnimatedBuilder(
animation: controller,
builder: (context, child) {
return const Padding(
padding: EdgeInsets.symmetric(
vertical: 2.0,
horizontal: 15.0),
);
}),
AnimatedBuilder(
animation: controller,
builder: (context, child) {
return Padding(
padding:
const EdgeInsets.symmetric(
vertical: 2.0,
horizontal: 15.0),
child: FloatingActionButton
.extended(
backgroundColor:
const Color(
0xffFAFAFA),
onPressed: () {
if (controller
.isAnimating) {
controller.stop();
setState(() {
LongBreak = false;
});
} else {
controller.reverse(
from: controller
.value ==
0
? 1.0
: controller
.value);
setState(() {
LongBreak = false;
});
}
},
icon: Icon(
controller.isAnimating
? Icons.pause
: Icons
.play_arrow,
color: const Color(
0xff3B3B3B),
),
label: Text(
controller.isAnimating
? "Pause"
: "Start",
style: const TextStyle(
color: Color(
0xff3B3B3B)),
)),
);
}),
],
),
),
],
),
),
),
],
);
}),
),
],
),
),
);
}
AnimationController _buildClockAnimation(TickerProvider tickerProvider) {
return AnimationController(
vsync: tickerProvider,
duration: const Duration(milliseconds: 750),
);
}
void _animateLeftDigit(
int prev,
int current,
AnimationController controller,
) {
final prevFirstDigit = (prev / 10).floor();
final currentFirstDigit = (current / 10).floor();
if (prevFirstDigit != currentFirstDigit) {
controller.forward();
}
}
}
How can I provide a height from the animation widget which respects the app bar widget and there is no lag when I started the timer?
What I mean is that I want to start the animation here:
Thank you for any help you can offer
As #Henrique Zanferrari suggested you are using height of the screen in
height: MediaQuery.of(context).size.height
Which is limiting the widgets to follow along with the appBar.
Try replacing this height with more general Widget like Expanded or Flexible like so.

How can I separate widgets on flutter without losing their properties?

For now I have this
I want to achieve this desing
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>
with TickerProviderStateMixin {
late TabController _tabController;
late Timer timer;
late AnimationController controller;
String get countText {
Duration count = controller.duration! * controller.value;
return controller.isDismissed
? '${controller.duration!.inHours.toString().padLeft(2, '0')}:${(controller.duration!.inMinutes % 60).toString().padLeft(2, '0')}:${(controller.duration!.inSeconds % 60).toString().padLeft(2, '0')}'
: '${count.inHours.toString().padLeft(2, '0')}:${(count.inMinutes % 60).toString().padLeft(2, '0')}:${(count.inSeconds % 60).toString().padLeft(2, '0')}';
}
#override
void initState() {
super.initState();
_tabController = TabController(length: 3, vsync: this);
}
#override
void dispose() {
_tabController.dispose();
super.dispose();
}
void notify() {
if (countText == '00:00:00') {
_tabController.animateTo(1, duration: const Duration(milliseconds: 300));
}
}
#override
Widget build(BuildContext context) {
return Container(
height: 600,
width: double.infinity,
child: DefaultTabController(
length: 3,
child: Scaffold(
appBar: AppBar(
elevation: 0,
backgroundColor: Colors.transparent,
bottom: PreferredSize(
preferredSize: Size.fromHeight(55),
child: Container(
color: Colors.transparent,
child: SafeArea(
child: Column(
children: <Widget>[
TabBar(
controller: _tabController,
indicator: 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: Color(0xff3B3B3B),
labelStyle: TextStyle(
fontSize: 12,
letterSpacing: 1.3,
fontWeight: FontWeight.w500),
unselectedLabelColor: Color(0xffD7D7D7),
tabs: [
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(
controller: _tabController,
children: <Widget>[
Center(
child: StartPomodoro(),
),
// Center(
// child: ShortBreak(),
// ),
// Center(child: LongBreak()),
],
),
bottomNavigationBar: Container(
height: 110,
color: Color(0xffFAFAFA),
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 15.0, vertical: 20),
child: GNav(
iconSize: 40,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
backgroundColor: Color(0xffFAFAFA),
color: Color(0xffD7D7D7),
activeColor: Color(0xff3B3B3B),
tabBackgroundColor: Color(0xffF0F0F0),
gap: 8,
onTabChange: (index) {
print(index);
},
padding: EdgeInsets.all(15),
tabs: [
GButton(
icon: Icons.settings,
text: 'Settings',
),
GButton(
icon: Icons.person,
text: "Profile",
),
GButton(
icon: Icons.task,
text: "Tasks",
),
GButton(
icon: Icons.show_chart,
text: "Performance",
),
],
),
),
),
)));
}
}
startpomodoro.dart
class StartPomodoro extends StatefulWidget {
const StartPomodoro({Key? key}) : super(key: key);
#override
State<StartPomodoro> createState() => _StartPomodoroState();
}
class _StartPomodoroState extends State<StartPomodoro>
with TickerProviderStateMixin {
List<bool> isSelected = [true, false];
late Timer timer;
late AnimationController controller;
String get countText {
Duration count = controller.duration! * controller.value;
return controller.isDismissed
? '${controller.duration!.inHours.toString().padLeft(2, '0')}:${(controller.duration!.inMinutes % 60).toString().padLeft(2, '0')}:${(controller.duration!.inSeconds % 60).toString().padLeft(2, '0')}'
: '${count.inHours.toString().padLeft(2, '0')}:${(count.inMinutes % 60).toString().padLeft(2, '0')}:${(count.inSeconds % 60).toString().padLeft(2, '0')}';
}
double progress = 1.0;
bool LongBreak = true;
void notify() {
if (countText == '00:00:00') {}
}
#override
void initState() {
super.initState();
controller = AnimationController(
vsync: this,
duration: const Duration(seconds: 0),
);
controller.addListener(() {
notify();
if (controller.isAnimating) {
setState(() {
progress = controller.value;
});
} else {
setState(() {
progress = 1.0;
LongBreak = true;
});
}
});
}
#override
void dispose() {
controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
ThemeData themeData = Theme.of(context);
return Scaffold(
backgroundColor:
LongBreak ? const Color(0xffD94530) : const Color(0xff6351c5),
body: GestureDetector(
onTap: () {
if (controller.isDismissed) {
showModalBottomSheet(
context: context,
builder: (context) => Container(
height: 300,
child: CupertinoTimerPicker(
initialTimerDuration: controller.duration!,
onTimerDurationChanged: (time) {
setState(() {
controller.duration = time;
});
},
),
),
);
}
},
child: AnimatedBuilder(
animation: controller,
builder: (context, child) {
return Stack(
children: <Widget>[
Align(
alignment: Alignment.bottomCenter,
child: Container(
color: const Color(0xffD94530),
height: controller.value *
MediaQuery.of(context).size.height *
0.640,
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Expanded(
child: Align(
alignment: Alignment.bottomCenter,
child: Align(
alignment: FractionalOffset.bottomCenter,
child: SingleChildScrollView(
child: Column(
mainAxisAlignment:
MainAxisAlignment.end,
crossAxisAlignment:
CrossAxisAlignment.center,
children: <Widget>[
Text(
countText,
style: const TextStyle(
fontSize: 90.0,
color: Color(0xffFAFAFA),
),
),
],
),
),
),
),
),
Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment:
CrossAxisAlignment.stretch,
children: [
AnimatedBuilder(
animation: controller,
builder: (context, child) {
return const Padding(
padding: EdgeInsets.symmetric(
vertical: 2.0,
horizontal: 15.0),
);
}),
AnimatedBuilder(
animation: controller,
builder: (context, child) {
return Padding(
padding: const EdgeInsets.symmetric(
vertical: 2.0,
horizontal: 15.0),
child:
FloatingActionButton.extended(
backgroundColor:
const Color(0xffFAFAFA),
onPressed: () {
if (controller
.isAnimating) {
controller.stop();
setState(() {
LongBreak = false;
});
} else {
controller.reverse(
from: controller
.value ==
0
? 1.0
: controller
.value);
setState(() {
LongBreak = false;
});
}
},
icon: Icon(
controller.isAnimating
? Icons.pause
: Icons.play_arrow,
color:
const Color(0xff3B3B3B),
),
label: Text(
controller.isAnimating
? "Pause"
: "Start",
style: const TextStyle(
color: Color(
0xff3B3B3B)),
)),
);
}),
const SizedBox(width: 20, height: 100),
],
),
),
],
),
),
],
);
}),
),
);}
}
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;
}
If I cut and paste the start widget to the HomePageTimerUI I cant do it well because the startpomodoro page has all the controllers to control the timer and animations, so how can I replace the start / pause button to the gnav widget maintaining its height and also initialize the timer?
Thank you for any help you can offer
It's difficult to precisely figure out from the code produced. Because of too many widgets and wrong indentations. I would give suggestions based on rough insights.
If you want to seperate two elements use Spacer that will do the job for you, Otherwise try the following approach.
Present approach (roughly)
Column
|_ Timer Container
|_ Floating button
to
Correct appraoch
Column
|_ Expanded
|_ Center
|_ Timer Container
|_ Floating Button

How to implement multiple AnimationControllers in flutter GetxController

I want to implement multiple animations with animation controllers by using getXController in flutter dart, but when I run the application it says animation controllers are paused and when I click on one of my TextFormFields animation controllers will work for just one second, and all of the data that I have entered in TextFormFields are removed.
This is my code LoginController code.
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
class LoginController extends GetxController with GetTickerProviderStateMixin //
{
late final Rx<AnimationController> _controller1;
late final Rx<AnimationController> _controller2;
late Rx<Animation<double>> _animation1;
late Rx<Animation<double>> _animation2;
late Rx<Animation<double>> _animation3;
late Rx<Animation<double>> _animation4;
get controller1 => _controller1.value;
set controller1(value) => _controller1.value = value;
get controller2 => _controller2.value;
set controller2(value) => _controller2.value = value;
get animation1 => _animation1.value;
set animation1(value) => _animation1.value = value;
get animation2 => _animation2.value;
set animation2(value) => _animation2.value = value;
get animation3 => _animation3.value;
set animation3(value) => _animation3.value = value;
get animation4 => _animation4.value;
set animation4(value) => _animation4.value = value;
#override
void onInit() {
super.onInit();
_controller1 = AnimationController(
vsync: this,
duration: const Duration(
seconds: 5,
),
).obs;
_animation1 = (Tween<double>(begin: .1, end: .15).animate(
CurvedAnimation(
parent: controller1,
curve: Curves.easeInOut,
),
)
..addListener(() => update())
..addStatusListener(
(status) {
if (status == AnimationStatus.completed) {
update([controller1.reverse()]);
} else if (status == AnimationStatus.dismissed) {
update([controller1.forward()]);
}
},
))
.obs;
_animation2 = (Tween<double>(begin: .02, end: .04).animate(
CurvedAnimation(
parent: controller1,
curve: Curves.easeInOut,
),
)..addListener(() => update()))
.obs;
_controller2 = AnimationController(
vsync: this,
duration: const Duration(
seconds: 5,
),
).obs;
_animation3 = (Tween<double>(begin: .41, end: .38).animate(
CurvedAnimation(
parent: controller2,
curve: Curves.easeInOut,
),
)
..addListener(() => update())
..addStatusListener(
(status) {
if (status == AnimationStatus.completed) {
update([controller2.reverse()]);
} else if (status == AnimationStatus.dismissed) {
update([controller2.forward()]);
}
},
))
.obs;
_animation4 = (Tween<double>(begin: 170, end: 190).animate(
CurvedAnimation(
parent: controller2,
curve: Curves.easeInOut,
),
)..addListener(() => update()))
.obs;
Timer(
const Duration(milliseconds: 2500),
() {
update([controller1.reverse()]);
},
);
update([controller1.forward()]);
}
#override
void onReady() {
super.onReady();
}
#override
void onClose() {
controller1.dispose();
controller2.dispose();
super.onClose();
}
}
and this is my GetViewPage.
void main() async {
WidgetsFlutterBinding.ensureInitialized();
runApp(
const MaterialApp(
home: LoginPage(),
title: 'Login Page',
),
);
SystemChrome.setSystemUIOverlayStyle(
const SystemUiOverlayStyle(
statusBarColor: Colors.transparent,
statusBarIconBrightness: Brightness.light,
),
);
}
class LoginPage extends GetView<LoginController> {
final String? action;
const LoginPage({Key? key, this.action}) : super(key: key);
#override
Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size;
final _userController = TextEditingController();
final _passwordController = TextEditingController();
return Scaffold(
backgroundColor: darkBackGround,
body: Form(
child: ScrollConfiguration(
behavior: Behavior(),
child: SingleChildScrollView(
child: SizedBox(
height: size.height,
child: Stack(
children: [
Positioned(
top: size.height * (controller.animation2.value + .58),
left: size.width * .21,
child: CustomPaint(
painter: CustomizedPainter(
radius: 50,
color1: darkLinearCustomPaintColor1,
color2: darkLinearCustomPaintColor2,
),
),
),
Positioned(
top: size.height * .98,
left: size.width * .1,
child: CustomPaint(
painter: CustomizedPainter(
radius: controller.animation4.value - 30,
color1: darkLinearCustomPaintColor1,
color2: darkLinearCustomPaintColor2,
),
),
),
Positioned(
top: size.height * .5,
left: size.width * (controller.animation2.value + .8),
child: CustomPaint(
painter: CustomizedPainter(
radius: 30,
color1: darkLinearCustomPaintColor1,
color2: darkLinearCustomPaintColor2,
),
),
),
Positioned(
top: size.height * controller.animation3.value,
left: size.width * (controller.animation1.value + .1),
child: CustomPaint(
painter: CustomizedPainter(
radius: 60,
color1: darkLinearCustomPaintColor1,
color2: darkLinearCustomPaintColor2,
),
),
),
Positioned(
top: size.height * .1,
left: size.width * .8,
child: CustomPaint(
painter: CustomizedPainter(
radius: controller.animation4.value,
color1: darkLinearCustomPaintColor1,
color2: darkLinearCustomPaintColor2,
),
),
),
Column(
children: [
Expanded(
flex: 5,
child: Padding(
padding: EdgeInsets.only(top: size.height * .1),
child: Text(
'',
style: TextStyle(
color: Colors.black.withOpacity(.7),
fontSize: 30,
fontWeight: FontWeight.bold,
letterSpacing: 1,
wordSpacing: 4,
fontFamily: 'pacific',
),
),
),
),
Expanded(
flex: 7,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
DarkThemeInput(
icon: Icons.account_circle_outlined,
hintText: 'User',
isPassword: false,
isEmail: false,
isPhone: false,
controller: _userController,
validator: (String? value) {
if (value!.isEmpty) {
return 'Enter your username or Email ';
}
return '';
},
),
DarkThemeInput(
icon: Icons.lock_outline,
hintText: 'Password...',
isPassword: true,
isEmail: false,
isPhone: false,
controller: _passwordController,
validator: (String? value) {
if (value!.isEmpty) {
return 'Password must be filled';
}
if (value.length < 6) {
return 'Enter more characters';
}
return '';
}),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
DarkThemeButton(
text: 'LOGIN', width: 2.58, onTap: () {}),
SizedBox(width: size.width / 20),
DarkThemeButton(
text: 'Forgotten password!',
width: 2.58,
onTap: () {},
),
],
),
],
),
),
Expanded(
flex: 6,
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: [
DarkThemeButton(
text: 'Create a new Account',
width: 2,
onTap: () {
Get.toNamed('/signup');
},
),
SizedBox(height: size.height * .05),
],
),
),
],
),
],
),
),
),
),
),
);
}
}
I want to notice that DarkThemeInput is a customized TextFormField and when I press them animation controllers work just for one second.

how to fix inverted 'ok' text in flatbutton?

I'm beginner in flutter. trying to design login page. when user clicks on 'ok' button then 'ok' button gets invisible and 'approved' named button gets visible using some animation. problem is that 'ok' displayed in inverted form as shown in pic. how dI correct this issue?
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
// TODO: implement build
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Animation class',
home: HomePage(),
);
}
}
class HomePage extends StatefulWidget {
#override
State<StatefulWidget> createState() => stateClass();
}
class stateClass extends State<HomePage> with SingleTickerProviderStateMixin {
AnimationController animationController;
Animation<double> animation;
Animation<double> sizeAnimation;
int currentState = 0;
#override
void initState() {
super.initState();
animationController = AnimationController(
duration: Duration(milliseconds: 1000), vsync: this);
animation = Tween<double>(begin: 0, end: 60).animate(animationController)
..addListener(() {
setState(() {});
});
sizeAnimation = Tween<double>(begin: 0, end: 1).animate(CurvedAnimation(
parent: animationController, curve: Curves.fastOutSlowIn))
..addListener(() {
setState(() {});
});
}
#override
Widget build(BuildContext context) {
// TODO: implement build
return Scaffold(
appBar: AppBar(
title: Text('Animation login'),
),
body: Container(
child: SingleChildScrollView(
child: Column(
children: <Widget>[
Container(
height: 100,
//color: Colors.black,
child: Center(
child: Image.asset('assets/fluttericon.png'),
)),
Container(
margin: EdgeInsets.all(10),
padding: EdgeInsets.all(10),
child: TextFormField(
decoration: InputDecoration(
labelText: 'enter your email id',
),
),
),
Container(
margin: EdgeInsets.all(10),
padding: EdgeInsets.all(10),
child: TextFormField(
obscureText: true,
decoration: InputDecoration(
labelText: 'enter your password',
),
),
),
Container(
// color: Colors.teal,
// height: 0,
child: Center(
child: Transform.scale(
scale: sizeAnimation.value - 1,
child: FlatButton(
onPressed: animationController.forward,
color: Colors.redAccent[200],
child: Text(
'ok',
style: TextStyle(fontSize: 17, color: Colors.black),
),
),
)),
),
Container(
//color: Colors.teal,
height: 80,
child: Center(
child: Transform.scale(
scale: sizeAnimation.value,
child: FlatButton(
onPressed: animationController.reverse,
color: Colors.redAccent[200],
child: Text(
'approved',
style: TextStyle(fontSize: 17, color: Colors.black),
),
),
)),
),
],
),
)
)
);
}
}
Text container
Container(
// color: Colors.teal,
// height: 0,
child: Center(
child: Transform.scale(
scale: sizeAnimation.value-1 ,
child: FlatButton(
onPressed: animationController.forward,
color: Colors.redAccent[200],
child: Text(
'ok',
style: TextStyle(fontSize: 17, color: Colors.black),
),
),
)),
),
When I change sizeAnimation.value-1 to sizeAnimation. then 'ok' word is in erect form. but ok button is not invisible.
Screenshot preview
A possible solution for what you are trying to do is to use Fade and Rotate transitions with separate controllers for each Text widget. Here's an example of that:
class ButtonTextFadeRotation extends StatefulWidget {
#override
_ButtonTextFadeRotationState createState() => _ButtonTextFadeRotationState();
}
class _ButtonTextFadeRotationState extends State<ButtonTextFadeRotation> with TickerProviderStateMixin {
AnimationController _okAnimationController;
AnimationController _approvedAnimationController;
#override
void initState() {
_okAnimationController = AnimationController(
vsync: this,
duration: Duration(milliseconds: 200),
value: 1
);
_approvedAnimationController = AnimationController(
vsync: this,
duration: Duration(milliseconds: 200),
value: 0
);
super.initState();
}
#override
Widget build(BuildContext context) {
return Center(
child: ClipRect(
child: RaisedButton(
onPressed: () {
_okAnimationController.reverse();
_approvedAnimationController.forward();
},
child: Stack(
alignment: Alignment.center,
children: <Widget>[
FadeTransition(
opacity: _okAnimationController,
child: RotationTransition(
turns: _okAnimationController,
alignment: Alignment.center,
child: Text('Ok'),
),
),
FadeTransition(
opacity: _approvedAnimationController,
child: RotationTransition(
turns: _approvedAnimationController,
alignment: Alignment.center,
child: Text('Approved'),
),
),
],
),
),
),
);
}
}