scrollController.animateTo Invoked Multiple Times Asyncronously - flutter

I Have RawKeyboardListener that invoke _getIDCardInfo each time KeyDown is press and hold.
the problem is _getIDCardInfo is invoked multiple times. this make problem because scrollcontroller.animateTo is animated only in the last time _getIDCardInfo is invoked.
I want scrollcontroller.animateTo is animated each time _getIDCardInfo is invoked, not only in the last call of _getIDCardInfo.
how can i make _getIdCardInfo() is invoked syncronously. or there is any way scrollController can be animated properly without getting invoked multiple times.
_getIdCardInfo() async {
try {
final String result = await platform.invokeMethod('scanIDCard');
await _myItems.insert(0, {"id": result});
await _scrollController.animateTo(
_scrollController.position.maxScrollExtent,
curve: Curves.easeIn,
duration: Duration(milliseconds: 30));
await _scrollController.animateTo(0,
curve: Curves.easeIn, duration: Duration(milliseconds: 50));
} on PlatformException catch (e) {
print(e);
}
}
#override
Widget build(BuildContext context) {
FocusNode _focusNode = FocusNode();
return RawKeyboardListener(
focusNode: _focusNode,
autofocus: true,
onKey: (RawKeyEvent event) async {
if ((event.runtimeType == RawKeyDownEvent &&
event.logicalKey.keyId == 1108101562648)) {
await _getIdCardInfo();
}
},
);
}
}

I guess what you need is to run animation recursively like this:
final _scrollController = ScrollController();
final _focusNode = FocusNode();
int _animateTimes = 0; // How many times we need to run an animation
Future<void> _animateFuture; // Is animation currently running
Future<void> _getIdCardInfo() async {
await _scrollController.animateTo(
Random().nextInt(1000).toDouble(),
duration: Duration(milliseconds: 1000),
curve: Curves.linear
);
_runNext();
}
void _runAnimation() {
_animateTimes++;
if (_animateFuture == null) {
_animateFuture = _getIdCardInfo();
}
}
void _runNext() {
_animateTimes--;
_animateFuture = null;
if (_animateTimes > 0) {
_animateFuture = _getIdCardInfo();
}
}
#override
void dispose() {
_focusNode.dispose(); // Don't forget to dispose of focus node
super.dispose();
}
...
RawKeyboardListener(
focusNode: _focusNode,
autofocus: true,
onKey: (RawKeyEvent event) async {
if (event is RawKeyDownEvent && event.logicalKey.keyId == 1108101562648) {
_runAnimation();
}
},
child: Text('asd')
),
...

Related

How to play lottie animation half?

Help me!
I want mute and unmute button with Lottie animation. But this mute.json json animation have both.
So I need one click play Lottie half animation like this.
When clicked and
#override
void initState() {
super.initState();
_controller = AnimationController(vsync: this)
..value = 0.5
..addListener(() {
setState(() {
// Rebuild the widget at each frame to update the "progress" label.
});
});
}
Column(
children[
Lottie.asset(
controller: _controller,
'assets/mute.json',
animate: true,
onLoaded: (composition) {
setState(() {
_controller.duration = composition.duration;
});
},
),
],
),
bool mute = false;
#override
void initState() {
super.initState();
// add duration
_controller = AnimationController(vsync: this, duration: Duration(milliseconds: 300));
}
use animateTo method on controller.
InkWell(
onTap: () {
mute = !mute;
log(mute.toString());
if (mute) {
_controller.animateTo(0.5);
} else {
_controller.animateTo(0);
}
},
child: LottieBuilder.network(
"https://maxst.icons8.com/vue-static/landings/animated-icons/icons/no-sound/no-sound.json",
controller: _controller,
height: 200,
),
),

How to start a timer when a new page is opened in flutter?

I have to start an OTP auto fill timer as soon as the page is opened. What is method by which the timer can be started as soon as the page is opened?
If you want your app to go back to previous view if the user could not enter the OTP before the time ends, you can use Tween builder inside your body content:
TweenAnimationBuilder(
tween: Tween(begin: 180.0, end: 0),
duration: Duration(seconds: 180),
builder: (context, value, child) {
double val = value as double;
int time = val.toInt();
return Text(
"Code Expires In $time"
style: TextStyle(
fontSize: FontConfig.appFontSize14,
color: ColorUtils.greyText,
fontWeight: FontWeight.w400,
),
);
},
onEnd: () {
Navigator.pop(context);
},
),
So, after 180 seconds the user will be sent back to previous screen.
or
If you are looking for a simple timer, just do the follow:
Timer? timer;
#override
void onInit() {
super.onInit();
timer = Timer.periodic(const Duration(seconds: 60), (Timer t) {
print('TImer active');
});
}
#override
void dispose() {
super.dispose();
if(timer != null) {
timer!.cancel();
}
}
use whichever fits your requirements
First create a void function for the timer in stateful widget.
void startTimer() {
const onsec = Duration(seconds: 1);
Timer timer = Timer.periodic(onsec, (timer) {
if(start == 0 ) {
setState(() {
timer.cancel();
});
}else{
setState(() {
start--;
});
}
});
}
Then call the function in override so the timer will start as soon as the page is opened
#override
void initState() {
super.initState();
startTimer();
}

Scroll To Top on Button Click in Flutter

I'm new to Flutter, I'm facing this issue,
There is a button below in ScrollController, And when I click it. It should scroll To top.
ScrollController is embedded in SingleChildScrollView
Widget build(BuildContext context) {
return Container(
child: SingleChildScrollView(
controller: scrollController)
);
}
I tried using
setState(() {
scrollController.animateTo(0.0,
duration: const Duration(milliseconds: 300),
curve: Curves.ease);
}
Below is the Whole Code
Future<void> nextPressed() async {
final bool connectivity = await ConnectivityManager().isInternetAvailable();
if(connectivity) {
final FormState form = _formKey.currentState;
if (form.validate()) {
form.save();
updateDOBError(_dobController.text);
setState(() {
if(dobErrorString.isEmpty){
_errorAlertVisibilityDOB = false;
}else{
_errorAlertVisibilityDOB = true;
}
_errorAlertVisibility = true;
});
} else {
updateDOBError(_dobController.text);
setState(() {
if(dobErrorString.isEmpty){
_errorAlertVisibilityDOB = false;
}else{
_errorAlertVisibilityDOB = true;
}
errorType = RegistrationErrorType.validationError;
_errorAlertVisibility = true;
});
--> scrollController.animateTo(0.0,
duration: const Duration(milliseconds: 300), curve: Curves.ease);
}
}else{
const SnackBar snackBar =
SnackBar(content: Text(AppStrings.no_internet));
Scaffold.of(context).showSnackBar(snackBar);
}}
The --> is the place, I'm facing issue
Required Result
Issue to be Fixed
Use following on button clicked
setState((){
_scrollController.animateTo(
_scrollController.position.minScrollExtent,
curve: Curves.easeOut,
duration: const Duration(milliseconds: 500),
);})

Scroll and controller priority flutter

i have a CupertinoModalBottomSheet that is closing by scrolling down. And in this page i have image that i can pinch by two fingers. Is there any way to change priority to pinching when two fingers on screen. Because when i put two fingers on screen i can still close the page by scrolling down until i start pinching.
class PinchZoom extends StatefulWidget {
final Widget child;
PinchZoom({
Key? key,
required this.child,
}) : super(key: key);
#override
State<PinchZoom> createState() => _PinchZoomState();
}
class _PinchZoomState extends State<PinchZoom>
with SingleTickerProviderStateMixin {
late TransformationController controller;
late AnimationController animationController;
Animation<Matrix4>? animation;
Map<int, Offset> touchPositions = <int, Offset>{};
final double minScale = 1;
final double maxScale = 2;
double scale = 1;
OverlayEntry? entry;
#override
void initState() {
super.initState();
controller = TransformationController();
animationController = AnimationController(
vsync: this,
duration: Duration(milliseconds: 200),
)
..addListener(() => controller.value = animation!.value)
..addStatusListener((status) {
if (status == AnimationStatus.completed) {
removeOverlay();
}
});
}
#override
void dispose() {
controller.dispose();
animationController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) =>
BlocBuilder<DishInfoCubit, DishInfoState>(
builder: (_, _state) {
final dishCubit = BlocProvider.of<DishInfoCubit>(context);
return Builder(
builder: (context) => Listener(
onPointerMove: (opm) {
savePointerPosition(opm.pointer, opm.position);
if (touchPositions.length > 1) {
dishCubit.setIsPinchInProgress(true);
}
},
onPointerDown: (opm) {
savePointerPosition(opm.pointer, opm.position);
if (touchPositions.length > 1) {
dishCubit.setIsPinchInProgress(true);
}
},
onPointerCancel: (opc) {
clearPointerPosition(opc.pointer);
if (touchPositions.length < 2) {
dishCubit.setIsPinchInProgress(false);
}
},
onPointerUp: (opc) {
clearPointerPosition(opc.pointer);
if (touchPositions.length < 2) {
dishCubit.setIsPinchInProgress(false);
}
},
child: InteractiveViewer(
transformationController: controller,
clipBehavior: Clip.none,
panEnabled: false,
minScale: minScale,
maxScale: maxScale,
onInteractionStart: (details) {
print("trying to start anim");
if (details.pointerCount < 2) return;
animationController.stop();
},
onInteractionEnd: (details) {
if (details.pointerCount != 1) return;
resetAnimation();
dishCubit.setIsPinchInProgress(false);
},
child: AspectRatio(
aspectRatio: 1,
child: widget.child,
),
),
),
);
},
);
void removeOverlay() {
entry?.remove();
entry = null;
}
void resetAnimation() {
animation = Matrix4Tween(
begin: controller.value,
end: Matrix4.identity(),
).animate(
CurvedAnimation(parent: animationController, curve: Curves.easeInOut),
);
animationController.forward(from: 0);
}
void savePointerPosition(int index, Offset position) {
setState(() {
touchPositions[index] = position;
});
}
void clearPointerPosition(int index) {
setState(() {
touchPositions.remove(index);
});
}
}

How to scroll flutter web app using arrow keys

I just build and deployed a flutter web app. The problem I encountered is that it doesn't scroll when I press arrow keys, also there is no scroll bar. (Only 2 figure gesture scrolling is possible)
I'm using SingleChildScrollView() with the column as its child.
Is there a way to implement them?
Or just one of them?
The code from Karan works, but when the app is in Debug Mode, instead of using the event.logicalKey.debugName == "Arrow Up", we could use event.logicalKey == LogicalKeyboardKey.arrowUp which works in both the debug and release mode.
class _MyKeyboardScrollingPageState extends State<MyKeyboardScrollingPage> {
final ScrollController _controller = ScrollController();
final FocusNode _focusNode = FocusNode();
void _handleKeyEvent(RawKeyEvent event) {
var offset = _controller.offset;
if (event.logicalKey == LogicalKeyboardKey.arrowUp) {
setState(() {
if (kReleaseMode) {
_controller.animateTo(offset - 200, duration: Duration(milliseconds: 30), curve: Curves.ease);
} else {
_controller.animateTo(offset - 200, duration: Duration(milliseconds: 30), curve: Curves.ease);
}
});
}
else if (event.logicalKey == LogicalKeyboardKey.arrowDown) {
setState(() {
if (kReleaseMode) {
_controller.animateTo(offset + 200, duration: Duration(milliseconds: 30), curve: Curves.ease);
} else {
_controller.animateTo(offset + 200, duration: Duration(milliseconds: 30), curve: Curves.ease);
}
});
}
}
#override
void dispose() {
_focusNode.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: RawKeyboardListener(
autoFocus = true,
focusNode = _focusNode,
onKey: _handleKeyEvent,
child: SingleChildScrollView(
controller: _controller,
child: SomeAwesomeWidget(),
),
),
);
}
}
I found one solution ...
Hope this helps someone with the same issue...
Using RawKeyboardListener(), we can listen to any keyboard stroke.
class _MyHomePageState extends State<MyHomePage> {
final ScrollController _controller = ScrollController();
final FocusNode _focusNode = FocusNode()
#override
void dispose() {
_focusNode.dispose();
super.dispose();
}
void _handleKeyEvent(RawKeyEvent event) {
var offset = _controller.offset; //Getting current position
if (event.logicalKey.debugName == "Arrow Down") {
setState(() {
if (kReleaseMode) {
//This block only runs when the application was compiled in release mode.
_controller.animateTo(offset + 50,
duration: Duration(milliseconds: 200), curve: Curves.ease);
} else {
// This will only print useful information in debug mode.
// print(_controller.position); to get information..
_controller.animateTo(offset + 50,
duration: Duration(milliseconds: 200), curve: Curves.ease);
}
});
} else if (event.logicalKey.debugName == "Arrow Up"){
setState(() {
if (kReleaseMode) {
_controller.animateTo(offset - 50,
duration: Duration(milliseconds: 200), curve: Curves.ease);
} else {
_controller.animateTo(offset - 50,
duration: Duration(milliseconds: 200), curve: Curves.ease);
}
});
#override
Widget build(BuildContext context) {
return Scaffold(
body: RawKeyboardListener(
autofocus: true,
focusNode: _focusNode,
onKey: _handleKeyEvent,
child: SingleChildScrollView(
controller: _controller,
child:...
}
}
You can wrap ScrollBar to SingleChildScrollView to show scroll bar, like this:
Scrollbar(
child: SingleChildScrollView(
child: Container(),
));
For the answers that are mentioned above to work, you need the following imports:
import 'package:flutter/services.dart';
import 'package:flutter/foundation.dart';
The Most simplest way to Scroll using mouse or Keyboard Arrow keys on Flutter Web is
ListView(
primary: true,
scrollDirection: Axis.vertical,
children:
No need to pass any ScrollController