How can I check the screen click status? - flutter

If nothing is done on the screen, I want to print something on the screen some time after the last action. How can I do that? How can I check the screen click status?

You can wrap Scaffold with GestureDetector and use onPanDown to capture the screen event, onTap doesn't win on hit test if there are inner clickable buttons. Also use behavior: HitTestBehavior.translucent,
Another notable thing is here, it is needed to be check on every second, because the checkup unit is on second. You can create a wrapper widget from it.
class ScreenT extends StatefulWidget {
const ScreenT({Key? key}) : super(key: key);
#override
State<ScreenT> createState() => _ScreenTState();
}
class _ScreenTState extends State<ScreenT> {
#override
void dispose() {
timer?.cancel();
super.dispose();
}
Timer? timer;
int maxDelaySec = 10;
int idleScreenCounter = 0;
#override
void initState() {
super.initState();
initTimer();
}
initTimer() {
timer = Timer.periodic(Duration(seconds: 1), (timer) {
idleScreenCounter++;
setState(() {}); //
});
}
onScreenTap() {
print("tapped on Screen");
idleScreenCounter = 0;
setState(() {});
}
#override
Widget build(BuildContext context) {
return GestureDetector(
behavior: HitTestBehavior.translucent,
onPanDown: (_) => onScreenTap(),
child: Scaffold(
body: LayoutBuilder(
builder: (context, constraints) => SizedBox(
width: constraints.maxWidth,
height: constraints.maxHeight,
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
if (maxDelaySec - idleScreenCounter > 0)
SizedBox(
height: 200,
child: Text(
" Tap the screen within ${maxDelaySec - idleScreenCounter}"),
),
if (maxDelaySec - idleScreenCounter < 0)
Container(
height: 100,
width: 100,
color: Colors.cyanAccent,
child: Text("Tap on screen"),
),
GestureDetector(
behavior: HitTestBehavior.translucent,
onTap: () {
print("An action");
},
child: Text("A Button"),
),
ElevatedButton(
onPressed: () {
print("act");
},
child: Text("Elev"),
)
],
),
),
),
),
),
);
}
}

A naive approach could involve a Timer with dart:async.
import 'dart:async';
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
#override
Widget build(BuildContext context) {
return const MaterialApp(
home: _SomeWidget(),
);
}
}
class _SomeWidget extends StatefulWidget {
const _SomeWidget();
#override
State<_SomeWidget> createState() => _SomeWidgetState();
}
class _SomeWidgetState extends State<_SomeWidget> {
late Timer _timer;
#override
void initState() {
super.initState();
// It's up to you if you want the timer to start immediately with some effects or not.
_timer = Timer(const Duration(seconds: 1), () {});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: GestureDetector(
onTap: () {
// i.e. from the first interaction and so on
_timer.cancel();
_timer = Timer(const Duration(seconds: 1), () {
if (mounted) {
// !
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Some message')),
);
}
});
},
child: const Center(child: Text('My screen contents')),
),
);
}
}
The mounted check is very important, as Timer introduces an async gap, which may be dangerous when using context.

You can add a Gesture detector at the top level and start a timer on tap and on completion you can fire an event like the following
GestureDetector(
onTap: (){
startTimer();
}
child: Column(
children:[
//all other widgets
]
)
),
Then to define the timer
late Timer _timer;
void startTimer()
{
if(_timer != null && _timer.isActive) _timer.cancel();
_timer = Timer(
const Duration(seconds: 30),
() {
print("inactive for 30 seconds");
},
);
}
here in this case each time the user taps on the screen the timer is restarted and on 30th second the print is fired.

Related

Lottie Animation works only once on tap

I am using Lottie Animation and want it to animate everytime I click on it , To this I am using GestureDetector However it only works the first time then for some reason it wont work again
Here is the code
import 'package:flutter/material.dart';
import 'package:lottie/lottie.dart';
void main() async {
runApp(const App());
}
class App extends StatefulWidget {
const App({super.key});
#override
State<App> createState() {
return _AppState();
}
}
class _AppState extends State<App> with SingleTickerProviderStateMixin {
late final AnimationController my_location_controller;
#override
void initState() {
// TODO: implement initState
super.initState();
my_location_controller =
AnimationController(vsync: this, duration: const Duration(seconds: 5));
}
#override
Widget build(BuildContext context) {
return MaterialApp(
color: Colors.lightBlue,
home: Scaffold(
backgroundColor: Colors.lightBlue,
body: Center(
child: SizedBox(
width: 300,
height: 300,
child: GestureDetector(
onTap: () {
my_location_controller.forward();
},
child: Lottie.asset(
'assets/my_location.json',
controller: my_location_controller,
animate: true,
repeat: true,
),
),
),
),
),
);
}
}
#Ante Bule thnx, will accept your answer and this seems to work too ..
child: GestureDetector(
onTap: () {
my_location_controller.reset();
my_location_controller.forward();
},
child: Lottie.asset(
'assets/my_location.json',
controller: my_location_controller,
),
Add a listener to reset your animation when it gets completed, like this:
#override
void initState() {
super.initState();
my_location_controller =
AnimationController(vsync: this, duration: const Duration(seconds: 5));
my_location_controller.addStatusListener((status) {
if (status == AnimationStatus.completed) {
my_location_controller.reset();
}
});
}

long press for incrementing value

I have an increment button with a callback function:
class IncrementButton extends StatelessWidget {
final VoidCallback callback;
const IncrementButton({
Key? key,
required this.callback,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
callback();
},
onLongPressStart: ((details) {
callback();
}),
......
);
}
}
And using it as:
IncrementButton(
callback: _increment,
),
The _increment function is as:
_increment() {
value += 1;
}
What I want is when the user taps once, the _increment function will be called. But when the user keeps a long press on the button, the same increment function should be called. But that doesn't happen. The _increment method is getting called only once for long Press
I guess you want the number to be incremented continually while long pressing the button/container. You need a timer which starts when onLongPress gets called. The timer stops when ´onLongPressEnd` gets called.
Try this:
import 'dart:async';
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatefulWidget {
const MyApp({super.key});
#override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
String action = "START";
Timer? timer;
int number = 0;
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Material App',
home: Scaffold(
appBar: AppBar(
title: const Text('Material App Bar'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(action),
SizedBox(height: 50),
Material(
borderRadius: BorderRadius.circular(20),
elevation: 20,
child: GestureDetector(
onTap: () => setState(() {
action = "Tap";
number++;
}),
onLongPress: () => setState(() {
timer = Timer.periodic(Duration(milliseconds: 50), (timer) {
setState(() {
action = "Longpress started";
number++;
});
});
}),
onLongPressEnd: (_) => setState(() {
action = "Longpress stopped";
timer?.cancel();
}),
child: Container(
alignment: Alignment.center,
decoration: BoxDecoration(
color: Colors.red[50],
borderRadius: BorderRadius.circular(20)
),
width: 100,
height: 100,
child: Text('Tap')
),
),
),
SizedBox(height: 50,),
Text(number.toString())
],
),
),
),
);
}
#override
void dispose() {
timer?.cancel();
super.dispose();
}
}
Proof:
Change your onlongpressStart.
do{
callback();
} while(btnPressed);
And on onlongpressEnd
setState(() => btnPressed = false);

Pass the context to the showdialog so that it is not lost when the start page loads

I am trying to display an information dialog when starting an application.
After closing, another window appears asking for permission. I call it all in the initState function. It works, but I noticed that this first info dialog also closes on its own when 15 seconds have elapsed. As I understand, this is because the application has loaded and the context is lost.
And when I change runApp(MyApp()) to runApp(MaterialApp(home: MyApp())). It works, the popup doesn't dissapear. But the other showdialogs on other pages didn't close automatically (Navigator.of(context).pop() and Navigator.pop(context) doesnt work.
How do I properly pass context to my initial showdialog so that it doesn't disappear when the start page loads?
void main() async {
WidgetsFlutterBinding.ensureInitialized();
runApp(MyApp());
}
class MyApp extends StatefulWidget {
static final navKey = new GlobalKey<NavigatorState>();
const MyApp({Key navKey}) : super(key: navKey);
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
#override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) async {
final context = MyApp.navKey.currentState.overlay.context;
await showDialogIfFirstLoaded(context);
await initPlatformState();
});
}
showDialogIfFirstLoaded(BuildContext context, prefs) async {
bool isFirstLoaded = prefs.getBool(keyIsFirstLoaded);
if (isFirstLoaded == null) {
return showDialog(
context: context,
builder: (BuildContext context) {
// return object of type Dialog
return new AlertDialog(
// title: new Text("title"),
content: new Text("//"),
actions: <Widget>[
new FlatButton(
child: new Text(".."),
onPressed: () {
Navigator.of(context).pop();
},
),
],
);
},
);
}
}
#override
Widget build(BuildContext context) {
return MaterialApp(
navigatorKey:MyApp.navKey,
home: new SplashScreen(),}
class SplashScreen extends StatefulWidget {
#override
_SplashScreenState createState() => new _SplashScreenState();
}
class _SplashScreenState extends State<SplashScreen> with SingleTickerProviderStateMixin {
Timer _timer;
bool _visible = true;
startTime() async {
_timer = Timer(new Duration(seconds: 5), navigationPage);
}
void navigationPage() {
Navigator.of(context).pushReplacementNamed('/home');
}
#override
void initState() {
_timer = Timer(Duration(seconds: 4),
() => setState(
() {
_visible = !_visible;
},
),
);
startTime();
super.initState();
}
#override
void dispose() {
_timer.cancel();
super.dispose();
}
#override
Widget build(BuildContext context) {
return new Stack(
children: <Widget>[
Container(
width: double.infinity,
child: Image.asset('images/bg.jpg',
fit: BoxFit.cover,
height: 1200,
),
),
Container(
width: double.infinity,
height: 1200,
color: Color.fromRGBO(0, 0, 0, 0.8),
),
Container(
alignment: Alignment.center,
child: Row(
children: <Widget>[
Expanded(
flex: 2,
child: Container(
child: Text(''),
),
),
],
),
),
],
);
}
}

How to implement a Custom or Flutter Loading Indicator

I have wrapped the body of the code below using GestureDetector, thus enabling me to use onVerticalDragEnd method available in the widget. When the app detects a Vertical Drag the _onRefreshing function is called, where it updates the Text widget after a delay of 2 seconds.
I want to include a Loading indicator while the _onRefreshing function is running.
How do I implement this task in Flutter?
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
dynamic balanceAvailable = 0;
void _onRefreshing(DragEndDetails details) async {
await Future.delayed(const Duration(seconds: 2));
if (details.velocity.pixelsPerSecond.dy > 0) {
setState(() {
balanceAvailable = 1000;
});
print('newbalance : $balanceAvailable');
print(details.velocity.pixelsPerSecond.dy);
}
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: GestureDetector(
onVerticalDragEnd: _onRefreshing,
child: Container(
width: double.infinity,
color: Colors.lightBlue,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
RaisedButton(
onPressed: () {},
child: Text("Button 1"),
),
SizedBox(height: 100.0),
Text('$balanceAvailable'),
],
),
),
),
),
);
}
}
You can return a CircularProgressIndicator inside a showdialog in your _onRefreshing method.
After the 2 seconds delay, you can remove it with Navigator.pop();
Maybe like this:
void _onRefreshing(DragEndDetails details) async {
showDialog(
context: context,
builder: (BuildContext context) {
return Center(
child: SizedBox(
height: MediaQuery.of(context).size.height/4,
width: MediaQuery.of(context).size.width/2,
child: CircularProgressIndicator(
valueColor: new AlwaysStoppedAnimation<Color>(Colors.red),
),
),
);
});
await Future.delayed(const Duration(seconds: 2));
if (details.velocity.pixelsPerSecond.dy > 0) {
setState(() {
balanceAvailable = 1000;
});
print('newbalance : $balanceAvailable');
print(details.velocity.pixelsPerSecond.dy);
}
Navigator.pop(context);
}

How to make a text become clickable after 30 secs in flutter?

I am making a login app where by i have created an OTP validation page. In this page i want to make a resend option which is clickable only after 30 seconds of page loading and once clicked becomes unclickable for ever.
I am new to flutter so I am sorry if this seems trivial.
You can follow this code.
class TestButton extends StatefulWidget {
#override
_TestButtonState createState() => _TestButtonState();
}
class _TestButtonState extends State<TestButton> {
bool firstStateEnabled = false;
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
Timer(Duration(milliseconds: 30000), () {
setState(() {
firstStateEnabled = true;
});
});
return Scaffold(
body: Container(
child: firstStateEnabled
? Center(
child: Container(
width: 200,
height: 55,
child: RaisedButton(
onPressed: () {},
child: Text("Resend OTP"),
),
),
)
: Center(child: Container()),
),
);
}
}
Or if you need only one time the button than you can follow below codes.
Install timer_count_down.
Then, below code.
class TestButton extends StatefulWidget {
#override
_TestButtonState createState() => _TestButtonState();
}
class _TestButtonState extends State<TestButton> {
bool firstStateEnabled = false;
final CountdownController controller = CountdownController();
final int seconds = 30;
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
Container(
child: firstStateEnabled
? Center(
child: Container(
width: (200),
height: 55,
child: RaisedButton(
onPressed: () {
setState(() {
firstStateEnabled = false;
});
},
child: Text("Resend OTP"),
),
),
)
: Center(child: Container()),
),
Countdown(
controller: controller,
seconds: seconds,
build: (context, double time) {
return Container();
},
interval: Duration(milliseconds: 100),
onFinished: () {
setState(() {
firstStateEnabled = true;
;
});
},
)
],
),
);
}
}