How can I implement a triple-click button in flutter?
On triple-click, the button will store an entry in Firebase database.
Its pretty easy. You can use a GestureDetector() to check for the number of taps then you can provide your logic if there are 3 taps.
GestureDetector(
onTap: () {
int now = DateTime.now().millisecondsSinceEpoch;
if (now - lastTap < 1000) {
print("Consecutive tap");
consecutiveTaps ++;
print("taps = " + consecutiveTaps.toString());
if (consecutiveTaps == 3){
// Do something
}
} else {
consecutiveTaps = 0;
}
lastTap = now;
},
child: ...
)
Implementing "triple click" button in flutter might not be possible. But, if you really want to make it work ASAP then a simple method could be to maintain a counter for the number of clicks done. Once the count reaches 3, you need to add your entry to Firestore.
I have modified the code from the counter app boilerplate of flutter.
Hope you have cloud_firestore in your pubspec.yaml file. If not then add it and put the services.json as well in the app folder of android or respective directory of ios.
cloud_firestore: ^0.13.4+1
So, now you can have a look at the code that I am using.
import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
_incrementCounter() {
setState(() {
_counter++;
});
if (_counter == 3) {
Firestore.instance
.collection('/sampleData')
.add({'data': "data"}).catchError((e) {
print(e);
});
setState(() {
_counter = 0;
});
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.display1,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
I have edited the _incrementCounter function. I added a conditional statement to check the _counter if it is 3 or not. Then, I am adding the Firestore entry. Later on, the most important bit is to set the _counter as 0 so that the next time the user presses the button 3 times, then the code will work accordingly. You can customize it according to your needs.
But remember, triple clicks have not been yet invented in flutter and this is just a work-around solution and don't use it for Real-life Development applications as this would be a very bad practice.
Related
I have some next (simplified) widget-tree structures:
- home (stack of widgets)
- page container
- editText field
As you can see, edit text will be always on top of the pages, which I will swap with Navigator.of(context) transactions.
The problem: when I focus editText the keyboard appears. When user starts typing, at some moment, I need to push another screen inside the page container. And when I use Navigator.of() - editText is unfocused and the keyboard goes down.
Whats needed: I need somehow to do transition (push/pop/replace) in page container with the Navigator and keep keyboard still up (keep editText focused).
Do you have any knowledge of how to do that?
Your use case looks really weird but I suppose you have a good reason to follow this setup.
Anyway the issue is that when Navigator.push is called, the TextField is unfocused. This is not a bug but rather an intended feature which (I think) is here to prevent a keyboard to follow you to the next screen where no TextField would be present. However I understand that this is an issue for your use case.
My solution (not the prettiest I would admit, but the only thing I could have working) is to force the focus back to the TextField manually. You need two component:
A FocusNode to associate to your TextField to listen to check if your TextField changes from focus to unfocused. If the focusOnNextUnfocus is set, it should re-request the focus after an unfocus
focusOnNextUnfocus a bool to set to true just before pushing, and which will be set to false once the focus has been requested again.
Here is the adaptation of the code you provided:
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
/// Handles the focus state of our text field
final focusNode = FocusNode();
/// A bool to keep track on whether we should refocus after
/// unfocus. This will be set to `true` before pushing
var focusOnNextUnfocus = false;
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
#override
void initState() {
focusNode.addListener(() {
// If we no longer have the focus and should refocus, we refocus
if (!focusNode.hasFocus && focusOnNextUnfocus) {
focusNode.requestFocus();
focusOnNextUnfocus = false; // Reset to false once it has been processed
}
});
super.initState();
}
#override
void dispose() {
focusNode.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
late BuildContext anotherContext;
final edit = TextField(
focusNode: focusNode,
onChanged: (String value) async {
if (value.length > 4) {
// Pop does not unfocus so no worries here
Navigator.pop(anotherContext);
} else if (value.length > 3) {
// Push DOES unfocus, so we need to set focusOnNextUnfocus to true
focusOnNextUnfocus = true;
Navigator.of(anotherContext).push(
MaterialPageRoute(builder: (_) {
return Builder(builder: (context) {
return Another();
});
}),
);
}
},
);
final scaffold = MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Builder(
builder: (context) {
anotherContext = context;
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
);
},
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
),
);
return Scaffold(
body: Stack(
children: [
Positioned(child: scaffold),
Align(
alignment: Alignment.center,
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 16),
child: edit,
),
),
],
),
);
}
}
class Another extends StatelessWidget {
const Another({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Container(
color: Colors.red,
);
}
}
I have an App made with flutter that when a button is pressed it increments a counter by 1. I would like to make the counter go to 10 and then simply start at 0 again. Is this possible?
this is code for the onPressed:
RaisedButton(
splashColor: Colors.blueAccent[700],
child: Text(
'+ Hit',
style: TextStyle(color: Colors.black),
),
color: Colors.lightBlue[100],
onPressed: _incrementCounter,
),
and the _incrementCounter section of code: (I am storing the value in shared prefs)
_incrementCounter() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
setState(() {
counter = (prefs.getInt('counter') ?? 0) + 1;
prefs.setInt('counter', counter);
});
}
How do i add the condition to start again when reaching 10??
Thanks to all
Add a condition on the onTap function of the button, where when counter > 10 then counter = 0
Do something like this in your function which is triggered on button click
void _incrementCounter() {
setState(() {
if (_counter == 10) {
_counter = 0;
} else {
_counter++;
}
});
}
_counter represents the variable which is holding count value
Sure it is.
you have to use an if satement which sets the counter back to zero once it has reached 10.
Run on DartPad
// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
// Your IF statement wgich defines when the counter goes back to zero
if(_counter>=10) {
_counter=0;
} else {
_counter++;
}
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
many thanks. This is now solved, apologies taken so long to post answer but have had to isolate (without laptop :( ),
Here is the code that works. Thanks again to the above contributions. You really helped
_incrementCounter() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
setState(() {
if (counter >= 10) {
counter = 1;
} else {
prefs.setInt('counter', counter++);
}
prefs.setInt('counter', counter);
});
}
I've created the simplest app: it's just a default project with added TextController and TextField. Nothing more. Here is a code:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
final TextEditingController _controller = new TextEditingController();
void _incrementCounter() {
setState(() {
_counter++;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
TextField(controller: _controller,),
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.display1,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
The actions I'm doing: tap on the text field, enter several symbols from the onscreen keyboard without committing, then press the switch app button. After that, I see the list of running apps with the current flutter app with the entered text. Then I click on the flutter app and entered text disappears.
I'm expecting that it's wrong behaviour. The text should not disappear.
Am I wrong? Or I have missed something? I'm new to flutter and everything here is very strange and it conflicts with practically all my previous experience.
yes it's expected because when you enter your widget it's state gets build, and you declared the variable in state class, so every time the state is build the _controller will be assigned to a new instance of TextEditingController and the previous instance of TextEditingController(which had your value inside of it) will be available for garbage collection.
I am new to flutter and building a sample app to learn it. In the above screenshot, I have created multiple widgets. My main widget contains the following widget.
Boy Girl Selector
Common Card
CounterButton (Plus or Minus)
Calculate Button
My main widget has two counter - age & weight.
CommonCard has below property :
incrementFunction() : I am setting this value from MainWidget as below.
decrementFunction()
ageIncrement() {
setState(() {
age++;
});
}
ageDecrement() {
setState(() {
age--;
});
}
value : age declared in main widget is passed to this value.
CounterButton has below property.
onPressed: increment or decrement function from parent widget is passed here through card widget.
If I keep whole code in main widget then it is working properly. But if I create multiple widget and pass increment and decrement function as argument in child widget onPressed on plus and minus is not working propely. Please share your thoughts. I am missing some fundamental of communication between child and parent widget.
There are different ways to achieve what you like as there are a couple of different state management techniques such as Dependency Injection, ChangeNotifier, BLoC, and so on (search for Flutter State Management for more details).
Here's an example of how you can achieve this on the famous counter example. This example is using dependency injection (we are passing the increment function to the a child widget as callback function). You can copy the code and past it on DartPad to quickly test it and see how it works:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void incrementCounter() {
setState(() {
_counter++;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.display1,
),
SizedBox(height: 50),
MySecondButton(secondButtonIncrement: incrementCounter),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
class MySecondButton extends StatelessWidget {
MySecondButton({Key key, this.secondButtonIncrement}) : super(key: key);
final VoidCallback secondButtonIncrement;
#override
Widget build(BuildContext context) {
return FlatButton(
child: Text("Second Button"),
onPressed: () {
secondButtonIncrement();
},
color: Colors.blue);
}
}
I hope that helps.
I am new to Flutter, and trying to understand how to to perform a series of actions based on a state of the widget.
I have a VERY basic app, based on the default new Flutter app with the clicks counter. I'd like that when the counter hits 10, the counter text highlights in red for 500ms (showing '10') and then the counter gets reset back to 0 and the text goes back to black (showing '0').
I was able to change the color to red when _counter==10, but unsure how to change it back to black and reset the counter after a set period of time. I'd also want to make the button "unclickable" during the 500ms.
It's simple just follow the code below,
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
bool isButtonEnabled = true;
Future<void> _incrementCounter() async {
_counter++;
if (_counter == 10) {
setState(() {
isButtonEnabled = false;
});
await Future.delayed(Duration(milliseconds: 500));
setState(() {
_counter = 0;
isButtonEnabled = true;
});
} else {
setState(() {});
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style:
TextStyle(color: isButtonEnabled ? Colors.black : Colors.red),
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: isButtonEnabled ? _incrementCounter : null,
tooltip: 'Increment',
child: Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
You're done.