Flutter, slider_button - flutter

I am new to Flutter and I am trying to create a draggable button to be able to accept or deny requests, I have used the Dtaggable Widget but it is giving me many problems, the last one is to limit the Dragable(button) to the container and therefore I've seen Dragabble is not designed for this and I should have used GestureDetector instead, but when I tried, I can't position the button in the center and let me take the distances well, if someone could give me a hand I would appreciate it.
class SliderButton extends StatefulWidget {
final double containerWidth;
final double containerHeight;
final Color containerColor;
final double buttonSize;
final Color buttonColor;
final Color textColor;
final double textSize;
final String leftText;
final String rightText;
final String textResultDeny;
final String textResultAccept;
final IconData icon;
final Color iconColor;
final Function(BuildContext context)? sliderPrimaryFunction;
final Function(BuildContext context)? sliderSecondaryFunction;
SliderButton(
{required this.containerWidth,
required this.containerHeight,
this.containerColor = Colors.black,
this.buttonSize = 50,
this.buttonColor = Colors.white,
this.rightText = 'Aceptar',
this.textResultAccept = 'Aceptado',
this.leftText = 'Denegar',
this.textResultDeny = 'Denegado',
this.sliderPrimaryFunction,
this.sliderSecondaryFunction,
this.textColor = Colors.white,
this.textSize = 24,
this.icon = Icons.add_circle_outline,
this.iconColor = Colors.black});
#override
_SliderButtonState createState() => _SliderButtonState();
}
class _SliderButtonState extends State<SliderButton> {
Offset position = Offset(0, 0);
bool started = false;
int switchOptions = 0;
Color? _containerColor = Colors.black;
#override
Widget build(BuildContext context) {
return Center(
child: sliderContainer(),
);
}
sliderContainer() => Container(
width: this.widget.containerWidth,
height: this.widget.containerHeight,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(50.00),
color: _containerColor,
boxShadow: [
BoxShadow(
color: Colors.black,
offset: Offset(0.0, 1.0),
blurRadius: 6.0,
),
],
),
child: sliderContainerContent());
sliderContainerContent() {
if (switchOptions == 0) return Center(child: containerContent());
if (switchOptions == 1)
return textResult(this.widget.textResultAccept);
else if (switchOptions == 2) return textResult(this.widget.textResultDeny);
}
containerContent() => Container(
width: this.widget.containerWidth,
height: this.widget.containerHeight,
child: Row(
children: [
started == false
? primaryText(this.widget.leftText, Alignment.centerLeft)
: Container(),
Center(
child: Container(
child: Draggable(
axis: Axis.horizontal,
feedback: roundedButton(),
child: started == false ? roundedButton() : Container(),
onDragStarted: () => setState(() {
started = true;
}),
onDragUpdate: (details) => _sequentialColor(details),
onDragEnd: (details) => _onSlideDragUpdate(details),
),
),
),
started == false
? primaryText(this.widget.rightText, Alignment.centerRight)
: Container(),
],
),
);
roundedButton() => Align(
child: Container(
width: this.widget.buttonSize,
height: this.widget.buttonSize,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: this.widget.buttonColor,
boxShadow: [
BoxShadow(
color: Colors.grey,
offset: Offset(0.0, 1.0),
blurRadius: 6.0,
),
],
),
child: Icon(this.widget.icon,
color: this.widget.iconColor, size: this.widget.buttonSize),
),
);
primaryText(String text, Alignment alignment) => Container(
alignment: alignment,
padding: EdgeInsets.all(14.0),
child: Text(text,
style: Theme.of(context)
.textTheme
.headline5
?.copyWith(color: Colors.white)),
);
textResult(String text) => Center(
child: Text(text,
style: Theme.of(context)
.textTheme
.headline3
?.copyWith(color: Colors.white)));
void _sequentialColor(DragUpdateDetails details) {
print(details.localPosition.dx);
var initColor = 200;
var algo = 240;
var algo2 = 200;
for (var i = details.localPosition.dx; i > algo; i++) {
setState(() {
_containerColor = Colors.green[initColor];
initColor += 100;
algo += 30;
});
}
for (var i = details.localPosition.dx; i < algo2; i--) {
setState(() {
_containerColor = Colors.red[initColor];
initColor += 100;
algo2 -= 30;
});
}
}
void _onSlideDragUpdate(DraggableDetails details) {
if (details.offset.distance > 470) {
setState(() {
switchOptions = 1;
_containerColor = Colors.lightGreen;
Future.delayed(const Duration(milliseconds: 500), () {
this.widget.sliderPrimaryFunction ?? Navigator.pop(context);
});
});
} else if (details.offset.distance < 400) {
setState(() {
switchOptions = 2;
_containerColor = Theme.of(context).errorColor;
Future.delayed(const Duration(milliseconds: 500), () {
this.widget.sliderPrimaryFunction ?? Navigator.pop(context);
});
});
} else
setState(() {
_containerColor = Theme.of(context).primaryColorDark;
started = false;
});
}
}

You can do this in this package.
https://pub.dev/packages/slider_button
Here is an example for you for the plugin. If you are having issue regarding the package. I will be happy to help you configure your own code
Center(child: SliderButton(
action: () {
///Do something here
Navigator.of(context).pop();
},
label: Text(
"Slide to cancel Event",
style: TextStyle(
color: Color(0xff4a4a4a), fontWeight: FontWeight.w500, fontSize: 17),
),
icon: Text(
"x",
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.w400,
fontSize: 44,
),
),
));

Thank you very much for the reply. I was trying to do it without the help of external packages, I ended up changing the Draggable for GestureDetector and it already works correctly.
Here I leave my solution in case it could be of help to someone.
import 'package:flutter/material.dart';
class SliderButton extends StatefulWidget {
final ValueChanged<double> valueChanged;
final double containerWidth;
final double containerHeight;
final Color containerColor;
final double buttonSize;
final Color buttonColor;
final Color textColor;
final double textSize;
final String leftText;
final String rightText;
final String textResultDeny;
final String textResultAccept;
final IconData icon;
final Color iconColor;
final Function(BuildContext context)? sliderPrimaryFunction;
final Function(BuildContext context)? sliderSecondaryFunction;
SliderButton({
required this.valueChanged,
required this.containerWidth,
required this.containerHeight,
this.containerColor = Colors.black,
this.buttonSize = 50,
this.buttonColor = Colors.white,
this.rightText = 'Aceptar',
this.textResultAccept = 'Aceptado',
this.leftText = 'Denegar',
this.textResultDeny = 'Denegado',
this.sliderPrimaryFunction,
this.sliderSecondaryFunction,
this.textColor = Colors.white,
this.textSize = 24,
this.icon = Icons.add_circle_outline,
this.iconColor = Colors.black,
});
#override
_SliderButtonState createState() => _SliderButtonState();
}
class _SliderButtonState extends State<SliderButton> {
ValueNotifier<double> valueListener = ValueNotifier(.0);
bool started = false;
int switchOptions = 0;
Color? _containerColor = Colors.black;
IconData _icon = Icons.lock;
#override
void initState() {
valueListener.addListener(notifyParent);
valueListener.value = 0.5;
super.initState();
}
void notifyParent() {
this.widget.valueChanged(valueListener.value);
}
#override
Widget build(BuildContext context) {
return Center(
child: sliderContainer(),
);
}
sliderContainer() => Stack(
children: [
Container(
width: this.widget.containerWidth,
height: this.widget.containerHeight,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(50.00),
color: _containerColor,
),
child: sliderContainerContent(),
),
Positioned(bottom: 2, child: slider())
],
);
sliderContainerContent() {
if (switchOptions == 0) return Center(child: containerContent());
if (switchOptions == 1)
return textResult(this.widget.textResultAccept);
else if (switchOptions == 2) return textResult(this.widget.textResultDeny);
}
containerContent() => Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
started == false ? primaryText(this.widget.leftText) : Container(),
started == false ? primaryText(this.widget.rightText) : Container(),
],
);
primaryText(String text) => Container(
padding: EdgeInsets.all(14.0),
child: Text(text,
style: Theme.of(context)
.textTheme
.headline5
?.copyWith(color: Colors.white)),
);
textResult(String text) => Center(
child: Text(text,
style: Theme.of(context)
.textTheme
.headline3
?.copyWith(color: Colors.white)));
slider() => Container(
width: this.widget.containerWidth,
height: this.widget.containerHeight,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(50.00),
color: Colors.transparent,
),
child: Builder(
builder: (context) {
final handle = gestureDetector();
return animatedBuilder(handle);
},
),
);
gestureDetector() => GestureDetector(
onHorizontalDragUpdate: _slideColor,
onHorizontalDragStart: (details) {
setState(() {
started = true;
});
},
onHorizontalDragEnd: _onSlideDragUpdate,
child: roundedButton(),
);
animatedBuilder(handle) => AnimatedBuilder(
animation: valueListener,
builder: (context, child) {
return AnimatedAlign(
duration:
Duration(milliseconds: valueListener.value == 0.5 ? 300 : 0),
alignment: Alignment(valueListener.value * 2 - 1, .5),
child: child,
);
},
child: handle,
);
roundedButton() => Container(
width: this.widget.buttonSize,
height: this.widget.buttonSize,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: this.widget.buttonColor,
boxShadow: [
BoxShadow(
color: Colors.grey,
offset: Offset(0.0, 1.0),
blurRadius: 6.0,
),
],
),
child: Icon(_icon,
color: this.widget.iconColor, size: this.widget.buttonSize - 5),
);
void _slideColor(DragUpdateDetails details) {
valueListener.value =
(valueListener.value + details.delta.dx / this.widget.containerWidth)
.clamp(.0, 1.0);
var sliderColor = 200;
var slideRight = 0.5;
var slideLeft = 0.5;
var i = valueListener.value;
for (; i > slideRight;) {
setState(() {
this._containerColor = Colors.green[sliderColor];
sliderColor += 100;
slideRight += 0.1;
_icon = Icons.lock_open_sharp;
});
}
for (; i < slideLeft;) {
setState(() {
this._containerColor = Colors.red[sliderColor];
sliderColor += 100;
slideLeft -= 0.1;
_icon = Icons.lock_outline_sharp;
});
}
}
void _onSlideDragUpdate(DragEndDetails details) {
if (valueListener.value >= 0.9) {
valueListener.value = 1;
setState(() {
switchOptions = 1;
_containerColor = Colors.lightGreen;
Future.delayed(const Duration(milliseconds: 500), () {
this.widget.sliderPrimaryFunction ?? Navigator.pop(context);
});
});
} else if (valueListener.value <= 0.1) {
valueListener.value = 0;
setState(() {
switchOptions = 2;
_containerColor = Theme.of(context).errorColor;
Future.delayed(const Duration(milliseconds: 500), () {
this.widget.sliderPrimaryFunction ?? Navigator.pop(context);
});
});
} else {
valueListener.value = 0.5;
setState(() {
_containerColor = Theme.of(context).primaryColorDark;
_icon = Icons.lock;
started = false;
});
}
}
}
'''
it still needs some improves but its working.

Related

Need to keep old values when I comeback from the second screen

I’m making a pomodoro timer app. When I go to the SettingsScreen and come back to the home screen it reset the setNum value to 0 and the done value to 0.
I need to keep those previous values when I come back from the SettingsScreen.
In the settings screen, If I changed
Navigator.of(context).push(MaterialPageRoute()
To this
Navigator.of(context).pop(MaterialPageRoute()
In home screen, it will keep the previous values and it won’t change setNum to 0 but then I cannot change Times from that screen.
How to solve this?
Home Screen
import 'dart:async';
import 'dart:ffi';
import 'package:audioplayers/audio_cache.dart';
import 'package:flutter/material.dart';
import 'package:percent_indicator/percent_indicator.dart';
import 'package:pomodoroapp/model/menu_item.dart';
import 'package:pomodoroapp/model/pomodoro_status.dart';
import 'package:pomodoroapp/screens/report_screen.dart';
import 'package:pomodoroapp/screens/settings_screen.dart';
import 'package:pomodoroapp/utils/constants.dart';
import 'package:pomodoroapp/widget/custom_button.dart';
import 'package:pomodoroapp/widget/menu_items.dart';
import 'package:pomodoroapp/widget/progress_icons.dart';
class Home extends StatefulWidget {
//////////////////////// passed (changed) values //////////////////////
final pomodoroTimeChanged;
final shortBreakTimeChanged;
final longBreakTimeChanged;
Home(
{Key key,
this.pomodoroTimeChanged,
this.shortBreakTimeChanged,
this.longBreakTimeChanged})
: super(key: key);
#override
State<Home> createState() => _HomeState(
pomodoroTimeChanged, shortBreakTimeChanged, longBreakTimeChanged);
}
//////////////////////// main button labels ////////////////////////
const _btnTextStart = 'START';
const _btnTextResumePomodoro = 'RESUME';
const _btnTextResumeBreak = 'RESUME';
const _btnTextStartShortBreak = 'START';
const _btnTextStartLongBreak = 'START';
const _btnTextStartNewSet = 'START NEW SET';
const _btnTextPause = 'PAUSE';
const _btnTextReset = 'RESET';
#override
class _HomeState extends State<Home> {
//////////////////////// values //////////////////////
int pomodoroTime;
int shortBreakTime;
int longBreakTime;
//////////////////////// default times //////////////////////
int pomodoroTimeDefault = 5;
int shortBreakTimeDefault = 2;
int longBreakTimeDefault = 3;
int pomodoriPerSet = 4;
int pomodoroTimeChanged;
int shortBreakTimeChanged;
int longBreakTimeChanged;
_HomeState(this.pomodoroTimeChanged, this.shortBreakTimeChanged,
this.longBreakTimeChanged);
static AudioCache player = AudioCache();
int remainingTime = pomodoroTotalTime;
String mainBtnText = _btnTextStart;
PomodoroStatus pomodoroStatus = PomodoroStatus.pausedPomodoro;
Timer _timer;
int pomodoroNum = 0;
int setNum = 0;
//////////////////////// dispose, to avoid memory leak //////////////////////
#override
void dispose() {
_cancelTimer();
super.dispose();
}
/////////////////////// Update state function for changed value ///////////////////////
_updateStatepomodoroTime() {
setState(() {
remainingTime = pomodoroTime;
});
}
_updateStateShortBreakTime() {
setState(() {
remainingTime = shortBreakTime;
});
}
_updateStateLongBreakTime() {
setState(() {
remainingTime = longBreakTime;
});
}
#override
void initState() {
super.initState();
player.load('bell.mp3');
//////////////////// setting an initial value //////////////////////
if (pomodoroTimeChanged != null) {
pomodoroTime = pomodoroTimeChanged;
_updateStatepomodoroTime();
} else {
pomodoroTime = pomodoroTimeDefault;
}
if (shortBreakTimeChanged != null) {
shortBreakTime = shortBreakTimeChanged;
//_updateStateShortBreakTime();
_updateStatepomodoroTime();
} else {
shortBreakTime = shortBreakTimeDefault;
}
if (longBreakTimeChanged != null) {
longBreakTime = longBreakTimeChanged;
//_updateStateLongBreakTime();
_updateStatepomodoroTime();
} else {
longBreakTime = longBreakTimeDefault;
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
extendBodyBehindAppBar: true,
backgroundColor: Colors.grey[900],
appBar: PreferredSize(
preferredSize: Size.fromHeight(27.0),
child: AppBar(
backgroundColor: Colors.transparent,
automaticallyImplyLeading: false,
iconTheme: IconThemeData(color: Colors.white, size: 10.0),
elevation: 0,
actions: [
PopupMenuButton<MenuItem>(
onSelected: (item) => onSelected(context, item),
itemBuilder: (context) =>
[...MenuItems.itemsFirst.map(buildItem).toList()],
)
],
),
),
body: Container(
width: double.infinity,
height: double.infinity,
decoration: const BoxDecoration(
image: DecorationImage(
image: NetworkImage(
'https://firebasestorage.googleapis.com/v0/b/flutterbricks-1926c.appspot.com/o/images%2Fwidgets%2F1634411682152%2FScreen%20Shot%202021-10-16%20at%203.14.09%20PM.png?alt=media&token=ec556af9-6dff-4020-a530-2b1eec58dafe'),
fit: BoxFit.cover,
),
),
child: Center(
child: Column(
children: [
Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
CircularPercentIndicator(
radius: 220.0,
lineWidth: 15.0,
percent: _getPomodoroPercentage(),
circularStrokeCap: CircularStrokeCap.round,
center: Text(
_secondsToFormatedString(remainingTime),
style:
const TextStyle(fontSize: 40, color: Colors.white),
),
progressColor: statusColor[pomodoroStatus],
),
const SizedBox(
height: 12,
),
ProgressIcons(
total: pomodoriPerSet,
done: pomodoroNum - (setNum * pomodoriPerSet),
),
const SizedBox(
height: 12,
),
Text(
'#$setNum',
style: const TextStyle(fontSize: 15, color: Colors.grey),
),
const SizedBox(
height: 5,
),
Text(
statusDescription[pomodoroStatus],
style: const TextStyle(color: Colors.white),
),
const SizedBox(
height: 12,
),
const SizedBox(
height: 12,
),
CustomButton(
onTap: _mainButtonPressed,
text: mainBtnText,
),
CustomButton(
onTap: _resetButtonPressed,
text: _btnTextReset,
)
],
),
)
],
),
),
),
);
}
_secondsToFormatedString(int seconds) {
int roundedMinutes = seconds ~/ 60;
int remainingSeconds = seconds - (roundedMinutes * 60);
String remainingSecondsFormated;
if (remainingSeconds < 10) {
remainingSecondsFormated = '0$remainingSeconds';
} else {
remainingSecondsFormated = remainingSeconds.toString();
}
return '$roundedMinutes:$remainingSecondsFormated';
}
_getPomodoroPercentage() {
int totalTime;
switch (pomodoroStatus) {
case PomodoroStatus.runingPomodoro:
totalTime = pomodoroTime;
break;
case PomodoroStatus.pausedPomodoro:
totalTime = pomodoroTime;
break;
case PomodoroStatus.runningShortBreak:
totalTime = shortBreakTime;
break;
case PomodoroStatus.pausedShortBreak:
totalTime = shortBreakTime;
break;
case PomodoroStatus.runningLongBreak:
totalTime = longBreakTime;
break;
case PomodoroStatus.pausedLongBreak:
totalTime = longBreakTime;
break;
case PomodoroStatus.setFinished:
totalTime = pomodoroTime;
break;
}
double percentage = (totalTime - remainingTime) / totalTime;
return percentage;
}
_mainButtonPressed() {
switch (pomodoroStatus) {
case PomodoroStatus.pausedPomodoro:
_startPomodoroCountdown();
break;
case PomodoroStatus.runingPomodoro:
_pausePomodoroCountdown();
break;
case PomodoroStatus.runningShortBreak:
_pauseShortBreakCountdown();
break;
case PomodoroStatus.pausedShortBreak:
_startShortBreak();
break;
case PomodoroStatus.runningLongBreak:
_pauseLongBreakCountdown();
break;
case PomodoroStatus.pausedLongBreak:
_startLongBreak();
break;
case PomodoroStatus.setFinished:
setNum++;
_startPomodoroCountdown();
break;
}
}
_startPomodoroCountdown() {
pomodoroStatus = PomodoroStatus.runingPomodoro;
_cancelTimer();
if (_timer != null) {
_timer.cancel();
}
_timer = Timer.periodic(
Duration(seconds: 1),
(timer) => {
if (remainingTime > 0)
{
setState(() {
remainingTime--;
mainBtnText = _btnTextPause;
})
}
else
{
_playSound(),
pomodoroNum++,
_cancelTimer(),
if (pomodoroNum % pomodoriPerSet == 0)
{
pomodoroStatus = PomodoroStatus.pausedLongBreak,
setState(() {
remainingTime = longBreakTime;
mainBtnText = _btnTextStartLongBreak;
}),
}
else
{
pomodoroStatus = PomodoroStatus.pausedShortBreak,
setState(() {
remainingTime = shortBreakTime;
mainBtnText = _btnTextStartShortBreak;
}),
}
}
});
}
_startShortBreak() {
pomodoroStatus = PomodoroStatus.runningShortBreak;
setState(() {
mainBtnText = _btnTextPause;
});
_cancelTimer();
_timer = Timer.periodic(
Duration(seconds: 1),
(timer) => {
if (remainingTime > 0)
{
setState(() {
remainingTime--;
}),
}
else
{
_playSound(),
remainingTime = pomodoroTime,
_cancelTimer(),
pomodoroStatus = PomodoroStatus.pausedPomodoro,
setState(() {
mainBtnText = _btnTextStart;
}),
}
});
}
_startLongBreak() {
pomodoroStatus = PomodoroStatus.runningLongBreak;
setState(() {
mainBtnText = _btnTextPause;
});
_cancelTimer();
_timer = Timer.periodic(
Duration(seconds: 1),
(timer) => {
if (remainingTime > 0)
{
setState(() {
remainingTime--;
}),
}
else
{
_playSound(),
remainingTime = pomodoroTime,
_cancelTimer(),
pomodoroStatus = PomodoroStatus.setFinished,
setState(() {
mainBtnText = _btnTextStartNewSet;
}),
}
});
}
_pausePomodoroCountdown() {
pomodoroStatus = PomodoroStatus.pausedPomodoro;
_cancelTimer();
setState(() {
mainBtnText = _btnTextResumePomodoro;
});
}
_resetButtonPressed() {
pomodoroNum = 0;
setNum = 0;
_cancelTimer();
_stopCountdown();
}
_stopCountdown() {
pomodoroStatus = PomodoroStatus.pausedPomodoro;
setState(() {
mainBtnText = _btnTextStart;
remainingTime = pomodoroTime;
});
}
_pauseShortBreakCountdown() {
pomodoroStatus = PomodoroStatus.pausedShortBreak;
_pauseBreakCountdown();
}
_pauseLongBreakCountdown() {
pomodoroStatus = PomodoroStatus.pausedLongBreak;
_pauseBreakCountdown();
}
_pauseBreakCountdown() {
_cancelTimer();
setState(() {
mainBtnText = _btnTextResumeBreak;
});
}
_cancelTimer() {
if (_timer != null) {
_timer.cancel();
}
}
_playSound() {
player.play('bell.mp3');
}
PopupMenuItem<MenuItem> buildItem(MenuItem item) => PopupMenuItem<MenuItem>(
value: item,
child: Row(children: [
Icon(
item.icon,
color: Colors.black,
size: 20,
),
const SizedBox(
width: 12,
),
Text(item.text)
]),
);
void onSelected(BuildContext context, MenuItem item) {
switch (item) {
case MenuItems.itemSettings:
Navigator.of(context).push(
MaterialPageRoute(builder: (context) => SettingsScreen()),
);
break;
case MenuItems.itemReport:
Navigator.of(context).push(
MaterialPageRoute(builder: (context) => ReportScreen()),
);
break;
}
}
}
class PopUpMenu extends StatelessWidget {
final List<PopupMenuEntry> menuList;
final Widget icon;
const PopUpMenu({Key key, this.menuList, this.icon}) : super(key: key);
#override
Widget build(BuildContext context) {
return PopupMenuButton(
itemBuilder: (context) => menuList,
icon: icon,
);
}
}
Settings Screen
import 'package:flutter/material.dart';
import 'package:numberpicker/numberpicker.dart';
import 'package:pomodoroapp/screens/home_screen.dart';
class SettingsScreen extends StatefulWidget {
const SettingsScreen({Key key}) : super(key: key);
#override
_SettingsScreen createState() => _SettingsScreen();
}
class _SettingsScreen extends State<SettingsScreen>
with TickerProviderStateMixin {
// Switch states
int _workSessionValue = 25;
int _shortBreakValue = 5;
int _longBreakValue = 15;
NumberPicker integerNumberPicker;
////////////////////// values to pass //////////////////////
int pomodoroTimeToChanged;
int shortBreakTimeToChanged;
int longBreakTimeToChanged;
// Work Session
_handleWorkValueChange(num value) {
if (value != null) {
setState(() {
_workSessionValue = value;
});
}
}
_handleWorkValueChangedExternally(num value) {
if (value != null) {
setState(() {
_workSessionValue = value;
});
integerNumberPicker.animateInt(value);
}
print('Updated pomodoro value: $_workSessionValue ');
pomodoroTimeToChanged = value * 60;
}
// Short break
_handleShortBreakValueChange(num value) {
if (value != null) {
setState(() {
_shortBreakValue = value;
});
}
}
_handleShortBreakValueChangedExternally(num value) {
if (value != null) {
setState(() {
_shortBreakValue = value;
});
integerNumberPicker.animateInt(value);
}
print('Updated short break value: $_shortBreakValue ');
shortBreakTimeToChanged = value * 60;
}
// Long Break
_handleLongBreakValueChange(num value) {
if (value != null) {
setState(() {
_longBreakValue = value;
});
}
}
_handleLongBreakChangedExternally(num value) {
if (value != null) {
setState(() {
_longBreakValue = value;
});
integerNumberPicker.animateInt(value);
}
print('Updated Long break value: $_longBreakValue ');
longBreakTimeToChanged = value * 60;
}
// Animation
AnimationController animationController;
String get timerString {
Duration duration =
animationController.duration * animationController.value;
return '${duration.inMinutes.toString().padLeft(2, '0')}\n${(duration.inSeconds % 60).toString().padLeft(2, '0')}';
}
#override
void initState() {
super.initState();
animationController = AnimationController(
vsync: this, duration: const Duration(seconds: 1500));
}
//number pick values
#override
Widget build(BuildContext context) {
integerNumberPicker = NumberPicker.integer(
initialValue: _workSessionValue,
minValue: 1,
maxValue: 50,
onChanged: _handleWorkValueChange,
);
integerNumberPicker = NumberPicker.integer(
initialValue: _shortBreakValue,
minValue: 1,
maxValue: 50,
onChanged: _handleShortBreakValueChange,
);
integerNumberPicker = NumberPicker.integer(
initialValue: _longBreakValue,
minValue: 1,
maxValue: 50,
onChanged: _handleLongBreakValueChange,
);
//UI
return Scaffold(
appBar: AppBar(
leading: IconButton(
icon: Icon(Icons.chevron_left),
onPressed: () => {
Navigator.of(context).push(MaterialPageRoute(
builder: (context) => Home(
pomodoroTimeChanged: pomodoroTimeToChanged,
shortBreakTimeChanged: shortBreakTimeToChanged,
longBreakTimeChanged: longBreakTimeToChanged,
)))
},
),
),
body: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
const Text("Timer",
textAlign: TextAlign.left,
style: TextStyle(
color: Colors.black,
fontSize: 32,
fontWeight: FontWeight.w700)),
const Divider(
thickness: 2,
color: Colors.black26,
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
const Text(
"Pomodoro",
style: TextStyle(
color: Colors.black,
fontSize: 22,
fontWeight: FontWeight.w400),
),
Row(
children: <Widget>[
Container(
width: 30,
height: 30,
child: RawMaterialButton(
shape: const CircleBorder(),
onPressed: _showWorkSessionDialog,
fillColor: Colors.amber,
elevation: 0,
child: Text(
"$_workSessionValue",
style: const TextStyle(
color: Colors.white,
fontSize: 18,
fontWeight: FontWeight.w500),
),
),
),
const Padding(
padding: EdgeInsets.all(5),
child: Text(
"min",
style: TextStyle(
color: Colors.black,
fontSize: 18,
fontWeight: FontWeight.w500),
),
)
],
)
],
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
const Text(
"Short Break",
style: TextStyle(
color: Colors.black,
fontSize: 22,
fontWeight: FontWeight.w400),
),
Row(
children: <Widget>[
Container(
width: 30,
height: 30,
child: RawMaterialButton(
shape: CircleBorder(),
onPressed: _showShortBreakDialog,
fillColor: Colors.amber,
elevation: 0,
child: Text(
"$_shortBreakValue",
style: const TextStyle(
color: Colors.white,
fontSize: 18,
fontWeight: FontWeight.w500),
),
),
),
const Padding(
padding: EdgeInsets.all(5),
child: Text(
"min",
style: TextStyle(
color: Colors.black,
fontSize: 18,
fontWeight: FontWeight.w500),
),
)
],
)
],
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
const Text(
"Long Break",
style: TextStyle(
color: Colors.black,
fontSize: 22,
fontWeight: FontWeight.w400),
),
Row(
children: <Widget>[
Container(
width: 30,
height: 30,
child: RawMaterialButton(
shape: CircleBorder(),
onPressed: _showLongBreakDialog,
fillColor: Colors.amber,
elevation: 0,
child: Text(
"$_longBreakValue",
style: const TextStyle(
color: Colors.white,
fontSize: 18,
fontWeight: FontWeight.w500),
),
),
),
const Padding(
padding: EdgeInsets.all(5),
child: Text(
"min",
style: TextStyle(
color: Colors.black,
fontSize: 18,
fontWeight: FontWeight.w500),
),
)
],
)
],
),
)
],
)
],
),
),
);
}
//dialog boxes
_showWorkSessionDialog() {
showDialog<int>(
context: context,
builder: (BuildContext context) {
return NumberPickerDialog.integer(
minValue: 0,
maxValue: 50,
initialIntegerValue: _workSessionValue,
title: const Text("Select a minute"),
);
},
).then(_handleWorkValueChangedExternally);
}
_showShortBreakDialog() {
showDialog<int>(
context: context,
builder: (BuildContext context) {
return NumberPickerDialog.integer(
minValue: 0,
maxValue: 50,
initialIntegerValue: _shortBreakValue,
title: const Text("Select a minute"),
);
},
).then(_handleShortBreakValueChangedExternally);
}
_showLongBreakDialog() {
showDialog<int>(
context: context,
builder: (BuildContext context) {
return NumberPickerDialog.integer(
minValue: 0,
maxValue: 50,
initialIntegerValue: _longBreakValue,
title: const Text("Select a minute"),
);
},
).then(_handleLongBreakChangedExternally);
}
}

Video player play image and video looping flutter

can video player play image for 10sec then loop to next video ?
How can i loop image only for 10sec then loop into next new video in my video clip data or video player controller ?
I manage to add video to video player but don't know how to add image to the player.
video clip data
class VideoClip {
final String fileName;
final String thumbName;
final String title;
final String parent;
int runningTime;
VideoClip(this.title, this.fileName, this.thumbName, this.runningTime, this.parent);
String videoPath() {
return "$parent/$fileName";
}
String thumbPath() {
return "$parent/$thumbName";
}
static List<VideoClip> remoteClips = [
VideoClip("For Bigger Fun", "ForBiggerFun.mp4", "images/ForBiggerFun.jpg", 0, "http://commondatastorage.googleapis.com/gtv-videos-bucket/sample"),
VideoClip("Elephant Dream", "ElephantsDream.mp4", "images/ForBiggerBlazes.jpg", 0, "http://commondatastorage.googleapis.com/gtv-videos-bucket/sample"),
VideoClip("BigBuckBunny", "BigBuckBunny.mp4", "images/BigBuckBunny.jpg", 0, "http://commondatastorage.googleapis.com/gtv-videos-bucket/sample"),
];
}
Video player controller
import 'dart:async';
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_video_list_sample/clips.dart';
import 'package:video_player/video_player.dart';
import 'package:wakelock/wakelock.dart';
class PlayPage extends StatefulWidget {
PlayPage({Key key, #required this.clips}) : super(key: key);
final List<VideoClip> clips;
#override
_PlayPageState createState() => _PlayPageState();
}
class _PlayPageState extends State<PlayPage> {
VideoPlayerController _controller;
List<VideoClip> get _clips {
return widget.clips;
}
var _playingIndex = -1;
var _disposed = false;
var _isFullScreen = false;
var _isEndOfClip = false;
var _progress = 0.0;
var _showingDialog = false;
Timer _timerVisibleControl;
double _controlAlpha = 1.0;
var _playing = false;
bool get _isPlaying {
return _playing;
}
set _isPlaying(bool value) {
_playing = value;
_timerVisibleControl?.cancel();
if (value) {
_timerVisibleControl = Timer(Duration(seconds: 2), () {
if (_disposed) return;
setState(() {
_controlAlpha = 0.0;
});
});
} else {
_timerVisibleControl = Timer(Duration(milliseconds: 200), () {
if (_disposed) return;
setState(() {
_controlAlpha = 1.0;
});
});
}
}
void _onTapVideo() {
debugPrint("_onTapVideo $_controlAlpha");
setState(() {
_controlAlpha = _controlAlpha > 0 ? 0 : 1;
});
_timerVisibleControl?.cancel();
_timerVisibleControl = Timer(Duration(seconds: 2), () {
if (_isPlaying) {
setState(() {
_controlAlpha = 0.0;
});
}
});
}
#override
void initState() {
Wakelock.enable();
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle.light);
_initializeAndPlay(0);
super.initState();
}
#override
void dispose() {
_disposed = true;
_timerVisibleControl?.cancel();
Wakelock.disable();
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle.dark);
_exitFullScreen();
_controller?.pause(); // mute instantly
_controller?.dispose();
_controller = null;
super.dispose();
}
void _toggleFullscreen() async {
if (_isFullScreen) {
_exitFullScreen();
} else {
_enterFullScreen();
}
}
void _enterFullScreen() async {
debugPrint("enterFullScreen");
await SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersive);
await SystemChrome.setPreferredOrientations([DeviceOrientation.landscapeLeft, DeviceOrientation.landscapeRight]);
if (_disposed) return;
setState(() {
_isFullScreen = true;
});
}
void _exitFullScreen() async {
debugPrint("exitFullScreen");
await SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge);
await SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
if (_disposed) return;
setState(() {
_isFullScreen = false;
});
}
void _initializeAndPlay(int index) async {
print("_initializeAndPlay ---------> $index");
final clip = _clips[index];
final controller = clip.parent.startsWith("http")
? VideoPlayerController.network(clip.videoPath())
: VideoPlayerController.asset(clip.videoPath());
final old = _controller;
_controller = controller;
if (old != null) {
old.removeListener(_onControllerUpdated);
old.pause();
debugPrint("---- old contoller paused.");
}
debugPrint("---- controller changed.");
setState(() {});
controller
..initialize().then((_) {
debugPrint("---- controller initialized");
old?.dispose();
_playingIndex = index;
_duration = null;
_position = null;
controller.addListener(_onControllerUpdated);
controller.play();
setState(() {});
});
}
var _updateProgressInterval = 0.0;
Duration _duration;
Duration _position;
void _onControllerUpdated() async {
if (_disposed) return;
// blocking too many updation
// important !!
final now = DateTime.now().millisecondsSinceEpoch;
if (_updateProgressInterval > now) {
return;
}
_updateProgressInterval = now + 500.0;
final controller = _controller;
if (controller == null) return;
if (!controller.value.isInitialized) return;
if (_duration == null) {
_duration = _controller.value.duration;
}
var duration = _duration;
if (duration == null) return;
var position = await controller.position;
_position = position;
final playing = controller.value.isPlaying;
final isEndOfClip = position.inMilliseconds > 0 && position.inSeconds + 1 >= duration.inSeconds;
if (playing) {
// handle progress indicator
if (_disposed) return;
setState(() {
_progress = position.inMilliseconds.ceilToDouble() / duration.inMilliseconds.ceilToDouble();
});
}
// handle clip end
if (_isPlaying != playing || _isEndOfClip != isEndOfClip) {
_isPlaying = playing;
_isEndOfClip = isEndOfClip;
debugPrint("updated -----> isPlaying=$playing / isEndOfClip=$isEndOfClip");
if (isEndOfClip && !playing) {
debugPrint("========================== End of Clip / Handle NEXT ========================== ");
final isComplete = _playingIndex == _clips.length - 1;
if (isComplete) {
print("played all!!");
if (!_showingDialog) {
_showingDialog = true;
_showPlayedAllDialog().then((value) {
_exitFullScreen();
_showingDialog = false;
});
}
} else {
_initializeAndPlay(_playingIndex + 1);
}
}
}
}
Future<bool> _showPlayedAllDialog() async {
return showDialog<bool>(
context: context,
barrierDismissible: true,
builder: (BuildContext context) {
return AlertDialog(
content: SingleChildScrollView(child: Text("Played all videos.")),
actions: <Widget>[
TextButton(
onPressed: () => Navigator.pop(context, true),
child: Text("Close"),
),
],
);
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: _isFullScreen
? null
: AppBar(
title: Text("Play View"),
),
body: _isFullScreen
? Container(
child: Center(child: _playView(context)),
decoration: BoxDecoration(color: Colors.black),
)
: Column(children: <Widget>[
Container(
child: Center(child: _playView(context)),
decoration: BoxDecoration(color: Colors.black),
),
Expanded(
child: _listView(),
),
]),
);
}
void _onTapCard(int index) {
_initializeAndPlay(index);
}
Widget _playView(BuildContext context) {
final controller = _controller;
if (controller != null && controller.value.isInitialized) {
return AspectRatio(
//aspectRatio: controller.value.aspectRatio,
aspectRatio: 16.0 / 9.0,
child: Stack(
children: <Widget>[
GestureDetector(
child: VideoPlayer(controller),
onTap: _onTapVideo,
),
_controlAlpha > 0
? AnimatedOpacity(
opacity: _controlAlpha,
duration: Duration(milliseconds: 250),
child: _controlView(context),
)
: Container(),
],
),
);
} else {
return AspectRatio(
aspectRatio: 16.0 / 9.0,
child: Center(
child: Text(
"Preparing ...",
style: TextStyle(color: Colors.white70, fontWeight: FontWeight.bold, fontSize: 18.0),
)),
);
}
}
Widget _listView() {
return ListView.builder(
padding: EdgeInsets.symmetric(horizontal: 12, vertical: 8),
itemCount: _clips.length,
itemBuilder: (BuildContext context, int index) {
return InkWell(
borderRadius: BorderRadius.all(Radius.circular(6)),
splashColor: Colors.blue[100],
onTap: () {
_onTapCard(index);
},
child: _buildCard(index),
);
},
).build(context);
}
Widget _controlView(BuildContext context) {
return Column(
children: <Widget>[
_topUI(),
Expanded(
child: _centerUI(),
),
_bottomUI()
],
);
}
Widget _centerUI() {
return Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
TextButton(
onPressed: () async {
final index = _playingIndex - 1;
if (index > 0 && _clips.length > 0) {
_initializeAndPlay(index);
}
},
child: Icon(
Icons.fast_rewind,
size: 36.0,
color: Colors.white,
),
),
TextButton(
onPressed: () async {
if (_isPlaying) {
_controller?.pause();
_isPlaying = false;
} else {
final controller = _controller;
if (controller != null) {
final pos = _position?.inSeconds ?? 0;
final dur = _duration?.inSeconds ?? 0;
final isEnd = pos == dur;
if (isEnd) {
_initializeAndPlay(_playingIndex);
} else {
controller.play();
}
}
}
setState(() {});
},
child: Icon(
_isPlaying ? Icons.pause : Icons.play_arrow,
size: 56.0,
color: Colors.white,
),
),
TextButton(
onPressed: () async {
final index = _playingIndex + 1;
if (index < _clips.length - 1) {
_initializeAndPlay(index);
}
},
child: Icon(
Icons.fast_forward,
size: 36.0,
color: Colors.white,
),
),
],
));
}
String convertTwo(int value) {
return value < 10 ? "0$value" : "$value";
}
Widget _topUI() {
final noMute = (_controller?.value?.volume ?? 0) > 0;
final duration = _duration?.inSeconds ?? 0;
final head = _position?.inSeconds ?? 0;
final remained = max(0, duration - head);
final min = convertTwo(remained ~/ 60.0);
final sec = convertTwo(remained % 60);
return Row(
children: <Widget>[
InkWell(
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 12, vertical: 8),
child: Container(
decoration: BoxDecoration(shape: BoxShape.circle, boxShadow: [
BoxShadow(offset: const Offset(0.0, 0.0), blurRadius: 4.0, color: Color.fromARGB(50, 0, 0, 0)),
]),
child: Icon(
noMute ? Icons.volume_up : Icons.volume_off,
color: Colors.white,
)),
),
onTap: () {
if (noMute) {
_controller?.setVolume(0);
} else {
_controller?.setVolume(1.0);
}
setState(() {});
},
),
Expanded(
child: Container(),
),
Text(
"$min:$sec",
style: TextStyle(
color: Colors.white,
shadows: <Shadow>[
Shadow(
offset: Offset(0.0, 1.0),
blurRadius: 4.0,
color: Color.fromARGB(150, 0, 0, 0),
),
],
),
),
SizedBox(width: 10)
],
);
}
Widget _bottomUI() {
return Row(
children: <Widget>[
SizedBox(width: 20),
Expanded(
child: Slider(
value: max(0, min(_progress * 100, 100)),
min: 0,
max: 100,
onChanged: (value) {
setState(() {
_progress = value * 0.01;
});
},
onChangeStart: (value) {
debugPrint("-- onChangeStart $value");
_controller?.pause();
},
onChangeEnd: (value) {
debugPrint("-- onChangeEnd $value");
final duration = _controller?.value?.duration;
if (duration != null) {
var newValue = max(0, min(value, 99)) * 0.01;
var millis = (duration.inMilliseconds * newValue).toInt();
_controller?.seekTo(Duration(milliseconds: millis));
_controller?.play();
}
},
),
),
IconButton(
padding: EdgeInsets.symmetric(horizontal: 12, vertical: 8),
color: Colors.yellow,
icon: Icon(
Icons.fullscreen,
color: Colors.white,
),
onPressed: _toggleFullscreen,
),
],
);
}
Widget _buildCard(int index) {
final clip = _clips[index];
final playing = index == _playingIndex;
String runtime;
if (clip.runningTime > 60) {
runtime = "${clip.runningTime ~/ 60}분 ${clip.runningTime % 60}초";
} else {
runtime = "${clip.runningTime % 60}초";
}
return Card(
child: Container(
padding: EdgeInsets.all(4),
child: Row(
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Padding(
padding: EdgeInsets.only(right: 8),
child: clip.parent.startsWith("http")
? Image.network(clip.thumbPath(), width: 70, height: 50, fit: BoxFit.fill)
: Image.asset(clip.thumbPath(), width: 70, height: 50, fit: BoxFit.fill),
),
Expanded(
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(clip.title, style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
Padding(
child: Text("$runtime", style: TextStyle(color: Colors.grey[500])),
padding: EdgeInsets.only(top: 3),
)
]),
),
Padding(
padding: EdgeInsets.all(8.0),
child: playing
? Icon(Icons.play_arrow)
: Icon(
Icons.play_arrow,
color: Colors.grey.shade300,
),
),
],
),
),
);
}
}

AutoScroll Text with pinch and zoom in flutter

I'm new to flutter and I'm trying to implement a page where I want to show Text Widget with AutoScroll Also users can Zoom in and Zoom Out the text. I have implemented AutoScroll using the help of this link (AutoScroll Text). I have also added GestureDetector but It's not working as expected. Here is the complete source code which I have right now...
import 'dart:async';
import 'dart:ui';
import 'package:ffmpeg_demo/Utility/util.dart';
import 'package:flutter/material.dart';
class PDFViewer extends StatefulWidget {
final Function() onDismissTapped;
final String pdfText;
const PDFViewer(
{Key? key, required this.onDismissTapped, required this.pdfText})
: super(key: key);
#override
State<PDFViewer> createState() => _PDFViewerState();
}
class _PDFViewerState extends State<PDFViewer> {
double scrollSpeed = 1.0;
Function? onDismissTapped;
//https://stackoverflow.com/questions/57138772/how-to-autoscroll-text-in-flutter
final ScrollController _scrollController = ScrollController();
bool scroll = false;
int speedFactor = 100;
//For Scaling Text Area
//https://stackoverflow.com/questions/55440184/flutter-gesturedetector-how-to-pinch-in-out-or-zoom-in-out-text-using-two-finge
double _fontSize = 20;
final double _baseFontSize = 20;
double _fontScale = 1;
double _baseFontScale = 1;
double _scaleFactor = 1.0;
double _baseScaleFactor = 1.0;
bool _stopScrolling = false;
_scroll() {
double maxExtent = _scrollController.position.maxScrollExtent;
double distanceDifference = maxExtent - _scrollController.offset;
double durationDouble = distanceDifference / speedFactor;
int duration = durationDouble.toInt();
if (duration <= 0) {
duration = 1;
}
print(
"maxScrollExtent = $maxExtent and distanceDifference = $distanceDifference and durationDouble = $durationDouble");
_scrollController.animateTo(_scrollController.position.maxScrollExtent,
duration: Duration(seconds: duration), curve: Curves.linear);
}
_toggleScrolling() {
/* if (_stopScrolling) {
print("Stop scrolling flag value is $_stopScrolling");
return;
} */
setState(() {
scroll = !scroll;
});
if (scroll) {
_scroll();
} else {
_scrollController.animateTo(_scrollController.offset,
duration: const Duration(seconds: 1), curve: Curves.linear);
}
}
#override
void initState() {
// TODO: implement initState
super.initState();
Future.delayed(const Duration(seconds: 1), () {
_toggleScrolling();
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.transparent,
body: Container(
color: Colors.transparent,
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 5.0, sigmaY: 5.0),
child: Container(
color: Colors.black.withOpacity(0.5),
child: Stack(
children: [
Padding(
padding: const EdgeInsets.fromLTRB(10, 50, 10, 100),
child: NotificationListener(
onNotification: (notif) {
if (notif is ScrollEndNotification && scroll) {
Timer(const Duration(seconds: 1), () {
_scroll();
});
}
return true;
},
child: SingleChildScrollView(
controller: _scrollController,
scrollDirection: Axis.vertical,
padding: const EdgeInsets.all(20),
child: DefaultTextStyle(
style: const TextStyle(color: Colors.white),
child: GestureDetector(
onScaleStart: (ScaleStartDetails scaleStartDetails) {
setState(() {
//_stopScrolling = true;
_toggleScrolling();
});
//_baseScaleFactor = _scaleFactor;
_baseFontScale = _fontScale;
print("base scale factor = $_baseFontScale");
},
onScaleUpdate:
(ScaleUpdateDetails scaleUpdateDetails) {
// don't update the UI if the scale didn't change
/* if (scaleUpdateDetails.scale == 1.0) {
debugPrint("Scale is 1.0 returning");
setState(() {
_stopScrolling = false;
_toggleScrolling();
});
return;
} */
/* _scaleFactor =
_baseScaleFactor * scaleUpdateDetails.scale;
print("Final scale factor = $_scaleFactor");
if (_scaleFactor < 1.0) {
_scaleFactor = 1.0;
}
print("New Final scale factor = $_scaleFactor"); */
//setState(() {
_fontScale =
(_baseFontScale * scaleUpdateDetails.scale)
.clamp(0.5, 5.0);
_fontSize = _fontScale * _baseFontSize;
debugPrint("New Final font size = $_fontSize");
if (_fontSize < 20) {
_fontSize = 20;
}
//});
setState(() {
//_toggleScrolling();
_stopScrolling = false;
_toggleScrolling();
});
},
child: Text(
widget.pdfText,
/* textScaleFactor:
(_scaleFactor <= 0.1) ? 1.0 : _scaleFactor, */
style: TextStyle(
fontSize: (_fontSize < 20) ? 20 : _fontSize),
),
),
),
),
),
),
Positioned(
bottom: 0,
child: Container(
height: 100,
width: deviceWidth(context),
color: Colors.black.withAlpha(50),
child: Stack(
children: [
Positioned(
bottom: 0,
width: deviceWidth(context),
child: Center(
child: TextButton(
child: const Text(
"Dismiss",
style: TextStyle(color: Colors.white),
),
onPressed: () => widget.onDismissTapped(),
),
),
),
Positioned(
top: 0,
width: deviceWidth(context),
child: Container(
height: 50,
width: deviceWidth(context),
child: _buildSlider(context)),
),
],
),
),
),
],
),
),
),
),
);
}
Widget _buildSlider(BuildContext context) {
return Container(
width: deviceWidth(context),
child: SliderTheme(
data: SliderThemeData(
activeTrackColor: Colors.brown[700],
inactiveTrackColor: Colors.brown[300],
inactiveTickMarkColor: Colors.transparent,
activeTickMarkColor: Colors.transparent),
child: Slider(
divisions: 100,
min: 0,
max: 100,
value: speedFactor.toDouble(),
onChanged: (double value) {
setState(() {
//scrollSpeed = value;
_toggleScrolling();
});
speedFactor = value.toInt();
if (speedFactor == 0) {
speedFactor = 1;
}
Future.delayed(const Duration(seconds: 1), () {
setState(() {
//scrollSpeed = value;
_toggleScrolling();
});
});
}),
),
);
}
}

How to create swipe button left and right?

How to create swipe button left and right ?
i have try create Swipe button to confirmation (swipe left to right)
my code:
class SwipeOnOffButton extends StatefulWidget {
final String timer;
final double timerSize;
final TextStyle timerStyle;
final double height;
final double width;
final Color backgroundColor;
final Color disableBackgroundColor;
final Color replaceBackgroundColor;
final Color foregroundColor;
final Color iconColor;
final IconData leftIcon;
final IconData rightIcon;
final double iconSize;
final String text;
final TextStyle textStyle;
final VoidCallback onConfirmation;
final BorderRadius foregroundRadius;
final BorderRadius backgroundRadius;
final bool disable;
const SwipeOnOffButton({
Key key,
this.timer,
this.timerSize,
this.timerStyle,
this.height = 48.0,
this.width = 300,
this.backgroundColor,
this.disableBackgroundColor,
this.foregroundColor,
this.iconColor,
this.leftIcon,
this.text,
this.textStyle,
#required this.onConfirmation,
this.foregroundRadius,
this.backgroundRadius,
this.rightIcon,
this.iconSize,
this.replaceBackgroundColor,
this.disable = false,
});
#override
State<StatefulWidget> createState() {
return SwipeOnOffButtonState();
}
}
class SwipeOnOffButtonState extends State<SwipeOnOffButton> {
double _position = 0;
int _duration = 0;
bool _isSwipe = false;
double getPosition() {
if (widget.disable) _position = 0;
if (_position < 0) {
return 0;
} else if (_position > widget.width - widget.height) {
return widget.width - widget.height;
} else {
return _position;
}
}
Color getColor() {
if (!widget.disable) {
if (_position > 0) {
return widget.replaceBackgroundColor ?? Colors.green;
} else {
return widget.backgroundColor ?? Colors.orange;
}
} else {
return widget.disableBackgroundColor ?? Colors.black;
}
}
void updatePosition(details) {
if (!widget.disable) {
if (details is DragEndDetails) {
setState(() {
_duration = 100;
_position = _isSwipe ? widget.width : 0;
});
} else if (details is DragUpdateDetails) {
setState(() {
_duration = 0;
_position = _isSwipe
? _position
: details.localPosition.dx - (widget.height / 2);
});
}
} else {
_position = 0;
}
}
void swipeReleased(details) {
if (_position > widget.width - widget.height) {
widget.onConfirmation();
_isSwipe = true;
}
updatePosition(details);
}
#override
Widget build(BuildContext context) {
return Container(
height: widget.height,
width: widget.width,
decoration: BoxDecoration(
borderRadius: widget.backgroundRadius ??
BorderRadius.all(Radius.circular(widget.height)),
color: !widget.disable
? widget.backgroundColor ?? Colors.orange
: Colors.black,
),
child: Stack(
children: <Widget>[
AnimatedContainer(
height: widget.height,
width: getPosition() + widget.height,
duration: Duration(milliseconds: _duration),
curve: Curves.bounceOut,
decoration: BoxDecoration(
borderRadius: widget.backgroundRadius ??
BorderRadius.all(Radius.circular(widget.height)),
color: getColor()),
),
Center(
child: Text(
widget.text ?? "Start",
style: widget.textStyle ??
ThemeData.textTheme.buttonText1
.copyWith(color: Colors.white),
),
),
AnimatedPositioned(
duration: Duration(milliseconds: _duration),
curve: Curves.bounceOut,
left: getPosition(),
child: GestureDetector(
onPanUpdate: (details) => updatePosition(details),
onPanEnd: (details) => swipeReleased(details),
child: !_isSwipe
? Container(
height: widget.height,
width: widget.height,
decoration: BoxDecoration(
borderRadius: widget.foregroundRadius ??
BorderRadius.all(
Radius.circular(widget.height / 2),
),
color: widget.foregroundColor ??
Colors.white.withOpacity(0.24),
),
child: Icon(
widget.leftIcon ?? Icons.arrow_circle_up_rounded,
color: widget.iconColor ?? Colors.white,
size: widget.iconSize ?? 20.0,
),
)
: Container(
height: widget.height,
width: widget.height,
decoration: BoxDecoration(
borderRadius: widget.foregroundRadius ??
BorderRadius.all(
Radius.circular(widget.height / 2),
),
color: widget.foregroundColor ??
Colors.white.withOpacity(0.24),
),
child: Icon(
widget.rightIcon ?? Icons.check_circle_rounded,
color: widget.iconColor ?? Colors.white,
size: widget.iconSize ?? 20.0,
),
),
),
),
],
),
);
}
}
but i need swipe button have value bool (swipe to right=true and swipe to left=false)
so, i try to improve my swipe button. but, i can't find solution
could you help me to fix this problem?
Use lite_rolling_switch package
LiteRollingSwitch(
value: true,
textOn: 'active',
textOff: 'inactive',
colorOn: Colors.deepOrange,
colorOff: Colors.blueGrey,
iconOn: Icons.lightbulb_outline,
iconOff: Icons.power_settings_new,
onChanged: (bool state) {
print('turned ${(state) ? 'on' : 'off'}');
},
),

Flutter canvas : how to avoid drawing outside image area

import 'dart:io';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_colorpicker/flutter_colorpicker.dart';
import 'dart:ui' as ui;
class EditImage extends StatefulWidget {
final String filePath;
EditImage({this.filePath});
#override
_EditImageState createState() => _EditImageState();
}
class _EditImageState extends State<EditImage> {
ui.Image decodedImage;
String newFilePath;
GlobalKey myCanvasKey = GlobalKey();
ImageEditor editor;
Color color = Colors.blue;
#override
void initState() {
loadImage(File(widget.filePath));
super.initState();
}
void loadImage(File image) async {
final data = await image.readAsBytes();
decodedImage = await decodeImageFromList(data);
editor = ImageEditor(image: decodedImage, strokeColor: color);
setState(() {});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
leading: InkWell(
onTap: () {
Navigator.pop(context, newFilePath ?? widget.filePath);
},
child: Icon(Icons.close),
),
//centerTitle: true,
title: Text('Edit'),
actions: [
InkWell(
onTap: () {
editor.undo();
myCanvasKey.currentContext.findRenderObject().markNeedsPaint();
},
child: Icon(Icons.undo),
),
SizedBox(
width: 10.0,
),
InkWell(
onTap: () async {
Color pickedColor;
bool isSelected = false;
await showDialog(
context: context,
child: AlertDialog(
contentPadding: const EdgeInsets.all(8.0),
title: const Text('Stroke Color'),
content: SingleChildScrollView(
child: ColorPicker(
pickerColor: color,
onColorChanged: (color) {
pickedColor = color;
},
enableAlpha: false,
showLabel: false,
pickerAreaHeightPercent: 0.6,
),
),
actions: <Widget>[
FlatButton(
child: const Text('Cancel'),
onPressed: () => Navigator.pop(context),
),
FlatButton(
child: const Text('Select'),
onPressed: () {
isSelected = true;
Navigator.of(context).pop();
},
),
],
),
);
if (isSelected) {
editor.updateStrokeColor(pickedColor);
setState(() {
color = pickedColor;
});
}
},
child: Container(
decoration: BoxDecoration(
//borderRadius: BorderRadius.circular(15.0),
border: Border.all(color: Colors.grey),
shape: BoxShape.circle,
color: color,
),
child: Padding(
padding: const EdgeInsets.all(5.0),
child: Container(
decoration: BoxDecoration(
color: Colors.black26, shape: BoxShape.circle),
child: Padding(
padding: const EdgeInsets.all(3.0),
child: Icon(
Icons.edit_outlined,
color: Colors.white,
semanticLabel: 'Stroke',
),
),
),
),
),
),
SizedBox(
width: 10.0,
),
InkWell(
onTap: () {
Navigator.pop(context, newFilePath ?? widget.filePath);
},
child: Icon(Icons.done),
),
SizedBox(
width: 10.0,
),
],
),
body: decodedImage == null
? Center(child: CircularProgressIndicator())
: Center(
child: FittedBox(
child: SizedBox(
height: decodedImage.height.toDouble(),
width: decodedImage.width.toDouble(),
child: GestureDetector(
onPanDown: (detailData) {
editor.update(detailData.localPosition);
myCanvasKey.currentContext
.findRenderObject()
.markNeedsPaint();
},
onPanUpdate: (detailData) {
editor.update(detailData.localPosition);
myCanvasKey.currentContext
.findRenderObject()
.markNeedsPaint();
},
onPanEnd: (detailData) {
editor.addNewPointsList();
},
child: CustomPaint(
key: myCanvasKey,
painter: editor,
),
),
),
),
),
);
}
}
class ImageEditor extends CustomPainter {
ImageEditor({
this.image,
this.strokeColor = Colors.black,
}) {
_strokes.add(_StrokeData());
}
final ui.Image image;
Color strokeColor;
List<_StrokeData> _strokes = [];
void updateStrokeColor(Color color) {
strokeColor = color;
}
void update(Offset offset) {
if (_strokes.last.color == null) _strokes.last.color = strokeColor;
_strokes.last.points.add(offset);
}
void addNewPointsList() {
print('pan end');
_strokes.add(_StrokeData());
}
void undo() {
if (_strokes.length > 1) _strokes.removeAt(_strokes.length - 2);
}
#override
void paint(Canvas canvas, Size size) {
canvas.drawImage(image, Offset.zero, Paint());
for (int i = 0; i < _strokes.length; i++) {
Paint _painter = Paint();
_painter.color = _strokes[i].color ?? Colors.transparent;
_painter.style = PaintingStyle.stroke;
_painter.strokeWidth = 15;
_painter.isAntiAlias = true;
for (int j = 0; j < _strokes[i].points.length; j++) {
if (j > 0)
canvas.drawLine(
_strokes[i].points[j - 1], _strokes[i].points[j], _painter);
}
}
}
#override
bool shouldRepaint(CustomPainter oldDelegate) {
return true;
}
}
class _StrokeData {
Color color;
List<Offset> points;
_StrokeData() {
points = List<Offset>();
}
}
How to restrict drawing within the image area ?
Use ClipRRect() as parent of FittedBox. This solves the problem
ClipRRect(child: FittedBox(child: SizedBox(...Painter widget goes here...) ))