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 am trying to make test project according to good practices.
Please note that I DON'T want any "hacky" approach. I am willing to learn good way of solving it.
My understanding of "lifting state up" is that any change updates the state, and then view is redrawn (rebuild) using current state. It is great in theory, but it DOES NOT work with TextFormField/TextEditingController.
I want to have a SharedState and bi-directonal TextFormField/TextEditingController, as follows:
case 1 (works):
TextFormField changes -> state is updated -> readonly Text (in WidgetTwo) is updated
case 2 (does not work):
button (in WidgetOne) is clicked -> state is updated -> TextFormField (in WidgetThree) shows new value from state
I have code in 3 different widgets + main file + SharedSate:
main.dart
void main() {
runApp(ChangeNotifierProvider(
create: (_) => sharedState(), child: const MyApp()));
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatelessWidget {
final String title;
const MyHomePage({Key? key, required this.title}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
WidgetOne(),
WidgetTwo(),
WidgetThree(),
]),
),
);
}
}
shared_state.dart
class SharedState extends ChangeNotifier {
int counter = 0;
void setCounter(int c) {
counter = c;
notifyListeners();
}
void incrementCounter() {
counter++;
notifyListeners();
}
void decrementCounter() {
counter--;
notifyListeners();
}
Future fetchCounterFromWeb() async {
// simulate external call
await Future.delayed(Duration(milliseconds: 500));
setCounter(42);
}
}
widget_one.dart
class WidgetOne extends StatelessWidget {
#override
Widget build(BuildContext context) {
var state = Provider.of<SharedState>(context, listen: false);
return Row(
children: [
ElevatedButton(
onPressed: () => state.decrementCounter(),
child: Text('decrement')),
ElevatedButton(
onPressed: () => state.incrementCounter(),
child: Text('increment')),
ElevatedButton(
onPressed: () => state.fetchCounterFromWeb(),
child: Text('fetch counter from web')),
],
);
}
}
widget_two.dart
class WidgetTwo extends StatelessWidget {
#override
Widget build(BuildContext context) {
var state = Provider.of<SharedState>(context, listen: true);
return Row(
children: [Text('Value of counter is: ${state.counter}')],
);
}
}
widget_three.dart (problem is here)
class WidgetThree extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return WidgetThreeState();
}
}
class WidgetThreeState extends State<WidgetThree> {
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
late TextEditingController _controller;
#override
void initState() {
super.initState();
var state = Provider.of<SharedState>(context, listen: false);
_controller = TextEditingController(text: state.counter.toString());
}
#override
Widget build(BuildContext context) {
var state = Provider.of<SharedState>(context, listen: true);
// THE ISSUE:
// It is NOT possible to update Controller (or TextEditing field)
// without this hacky line (which is not good practice)
_controller.text = state.counter.toString();
return Form(
key: _formKey,
child: Column(children: [
TextFormField(
controller: _controller,
keyboardType: TextInputType.number,
onChanged: (v) {
state.setCounter(int.parse(v.isEmpty ? '0' : v));
},
)
]),
);
}
}
I know I can possible move TextEditingController to SharedState, but SharedState should be UI agnostic, and TextEditingController is a UI widget.
when pressed on quick search the container should expand like this and I don't want to use expandable or any other widget because I want to use the animation of animated container when opened and closed
All credit goes to this post answer: How to create an animated container that hide/show widget in flutter
Here is a Complete Working Code:
// ignore_for_file: prefer_const_constructors
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
// This is the theme of your application.
//
// Try running your application with "flutter run". You'll see the
// application has a blue toolbar. Then, without quitting the app,
try
// changing the primarySwatch below to Colors.green and then invoke
// "hot reload" (press "r" in the console where you ran "flutter run",
// or simply save your changes to "hot reload" in a Flutter IDE).
// Notice that the counter didn't reset back to zero; the
application
// is not restarted.
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Animated Container Demo'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
// This widget is the home page of your application. It is stateful,
meaning
// that it has a State object (defined below) that contains fields that
affect
// how it looks.
// This class is the configuration for the state. It holds the values
(in
this
// case the title) provided by the parent (in this case the App widget)
and
// used by the build method of the State. Fields in a Widget subclass
are
// always marked "final".
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
double _height = 50.0;
bool _isExpanded = false;
Future<bool> _showList() async {
await Future.delayed(Duration(milliseconds: 300));
return true;
}
#override
Widget build(BuildContext context) {
// This method is rerun every time setState is called, for instance as
done
// by the _incrementCounter method above.
//
// The Flutter framework has been optimized to make rerunning build
methods
// fast, so that you can just rebuild anything that needs updating
rather
// than having to individually change instances of widgets.
return Scaffold(
appBar: AppBar(
// Here we take the value from the MyHomePage object that was
created by
// the App.build method, and use it to set our appbar title.
title: Text(widget.title),
),
body: AnimatedContainer(
duration: Duration(milliseconds: 300),
height: _height,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(5),
color: Colors.grey,
),
width: MediaQuery.of(context).size.width - 100,
padding: EdgeInsets.only(left: 15, right: 15),
child: Column(
children: [
Padding(
padding: const EdgeInsets.only(top: 10),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('Title'),
InkWell(
onTap: () {
if (!_isExpanded) {
setState(() {
_height = 300;
_isExpanded = true;
});
} else {
setState(() {
_height = 50;
_isExpanded = false;
});
}
},
child: Container(
height: 30,
width: 40,
color: Colors.red,
child:
!_isExpanded ? Icon(Icons.add) :
Icon(Icons.remove),
),
),
],
),
),
_isExpanded
? FutureBuilder(
future: _showList(),
/// will wait untill box animation completed
builder: (context, snapshot) {
if (!snapshot.hasData) {
return SizedBox();
}
return ListView.builder(
itemCount: 10,
shrinkWrap: true,
itemBuilder: (context, index) {
return Text('data'); // your custom UI
},
);
})
: SizedBox.shrink(),
],
),
));
}
}
use expandable: flutter pub add expandable,
check the documentation here.
This is my storage. I want the image to shown on my app directly. THE REQUEST REACHES the storage, I checked from the statistics. But cannot take it, also I changed rules to that everyone can download them. But still nothing. I tried solutions from this page: Flutter Load Image from Firebase Storage
But they didn't work at all. Maybe I've put the codes in the wrong places, I don't know. How do I do this? Can you please help? Here's my whole code of main.dart:
import 'package:firebase_storage/firebase_storage.dart';
import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_core/firebase_core.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.red,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
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;
var url;
void _incrementCounter() {
setState(() {
_counter++;
});
}
Widget _buildListItem(BuildContext context, DocumentSnapshot document) {
String mezunD;
if (document.data()['mezunDurumu'] == false) {
mezunD = "mezun değil";
}
if (document.data()['mezunDurumu'] == true) {
mezunD = "mezun";
}
var listTile = ListTile(
title: Column(
children: [
Expanded(
child: Text(
"Ad Soyad: " + document.data()['adSoyad'],
),
),
Expanded(
child: Text(
"Yaş: " + document.data()['yas'].toString(),
),
),
Expanded(
child: Text(
"Doğum Tarihi: " + document.data()['dogumTarihi'],
),
),
Expanded(
child: Text("Mezun Durumu: " + mezunD),
),
//I wanna show image right under these.
],
),
);
return listTile;
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Öğrenci Durumu"),
),
body: StreamBuilder(
stream: FirebaseFirestore.instance.collection('tablo').snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) return const Text('Loading...');
return ListView.builder(
itemExtent: 100.0,
itemCount: snapshot.data.documents.length,
itemBuilder: (context, index) =>
_buildListItem(context, snapshot.data.documents[index]),
);
}),
);
}
}
If you click in the firebase storage on the image then you can see on right side a preview of the image. under this preview is a blue link. if you tap on the blue link you open the image in full-size in the browser with the full image-address including the access-token. copy the full url of this browser window and use it in flutter. for example with:
Image.network(url);
or the package "cached_network_image"
Im trying to create a flutter app with a simple raised button that does the following:
sends an sms in the background using the sms package opens a webpage
2. in the app(only for 5 seconds) using url_launcher opens the phones
3. native app for making a voice call with the onPressed property.
And I wanted it to be in this order so that I can make the phone call at the end. However, the inside the onPressed opens the native phone call app first, which doesnt let my web page open unless I exit out of the phone call app.
Im having a hard time understanding why the phone call native app is opened first, even though I make the call the _makePhoneCall() method only after I make the _launchInApp(toLaunch) call. sendSMS() is being called correctly
How can I set this in a way that the phone call native app is called only after the webpage is opened in the app and follows the order? Any help would be great
Below is the piece of code:
import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:sms/sms.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Packages testing',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Packages testing'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
String _phone = '';
_launchInApp(String url) async {
if (await canLaunch(url)) {
await launch(
url,
forceSafariVC: true,
forceWebView: true,
headers: <String, String>{'my_header_key': 'my_header_value'},
);
} else {
throw 'Could not launch $url';
}
}
_makePhoneCall(String url) async {
if (await canLaunch(url)) {
await launch(url);
} else {
throw 'Could not launch $url';
}
}
void sendSMS() {
SmsSender sender = new SmsSender();
sender.sendSms(new SmsMessage(_phone, 'Testing Handset'));
}
#override
Widget build(BuildContext context) {
const String toLaunch = 'https://flutter.dev/';
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: ListView(
children: <Widget>[
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Padding(
padding: const EdgeInsets.all(16.0),
child: TextField(
onChanged: (String text) => _phone = text,
decoration:
const InputDecoration(hintText: 'Phone Number')),
),
FlatButton(
onPressed: () => setState(() {
sendSMS();
_launchInApp(toLaunch);
_makePhoneCall('tel:$_phone');
}),
child: const Text('Run All'),
),
const Padding(padding: EdgeInsets.all(16.0)),
],
),
],
),
);
}
}
You will have to use the await keyword before the _launchInApp function to make it work properly. Try the following code.
FlatButton(
onPressed: () aync {
sendSMS();
await _launchInApp(toLaunch);
_makePhoneCall('tel:$_phone');
}),
child: const Text('Run All'),
),
You created async functions but when you called them you did not specify that you want to wait for them to complete. Add the await keyword in OnPressed
This question already has answers here:
How to take a screenshot of the current widget - Flutter
(4 answers)
Closed 2 years ago.
How can I implement the in-app screenshot functionality in flutter android?
I need this function to take a screenshot of the app screen and share the picture.
are there any plugins?
I have shared an example of code where I have used Screenshot plugin available on pub.dev along with permission handler and path provider plugin. Basically this plugin wraps your widgets inside RenderRepaintBoundary and creates an screenshot of your widget.
main.dart
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:image_gallery_saver/image_gallery_saver.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:screenshot/screenshot.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: 'Screenshot 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;
File _imageFile;
//Create an instance of ScreenshotController
ScreenshotController screenshotController = ScreenshotController();
#override
void initState() {
// TODO: implement initState
super.initState();
_requestPermission();
}
_requestPermission() async {
Map<Permission, PermissionStatus> statuses = await [
Permission.storage,
].request();
final info = statuses[Permission.storage].toString();
print(info);
}
void _incrementCounter() {
setState(() {
_counter++;
});
}
#override
Widget build(BuildContext context) {
// This method is rerun every time setState is called, for instance as done
// by the _incrementCounter method above.
//
// The Flutter framework has been optimized to make rerunning build methods
// fast, so that you can just rebuild anything that needs updating rather
// than having to individually change instances of widgets.
return Scaffold(
appBar: AppBar(
// Here we take the value from the MyHomePage object that was created by
// the App.build method, and use it to set our appbar title.
title: Text(widget.title),
),
body: Container(
child: new Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Screenshot(
controller: screenshotController,
child: Column(
children: <Widget>[
Text(
'You have pushed the button this many times:' +
_counter.toString(),
),
FlutterLogo(),
],
),
),
_imageFile != null ? Image.file(_imageFile) : Container(),
],
),
),
),
floatingActionButton: FloatingActionButton(
onPressed: () async {
_incrementCounter();
_imageFile = null;
screenshotController
.capture(delay: Duration(milliseconds: 10))
.then((File image) async {
//print("Capture Done");
setState(() {
_imageFile = image;
});
final result =
await ImageGallerySaver.saveImage(image.readAsBytesSync());
print("File Saved to Gallery $result");
}).catchError((onError) {
print("Error: $onError");
});
},
tooltip: 'Increment',
child: Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
_saved(File image) async {
final result = await ImageGallerySaver.saveImage(image.readAsBytesSync());
print("File Saved to Gallery");
}
}
Packages used:
screenshot:
image_gallery_saver: ^1.1.0
permission_handler:
path_provider: ^1.6.24
You will need to specify storage permissions in AndroidManifest file as below:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
To take screenshots within flutter app of the current use this screenshot package it also has example and documentation for saving the image to a folder and as well as to gallery which can also be google photos using 2 other plugins which you can find there only. I have used this plugin and it works really well if you havep any queries then you can comment below. Hope this helps you out!