I have been trying to figure out how to get my flutter dialog to disappear. I have tried everything and nothing I do will get rid of it until after the method finishes processing.
Background: Make an application that allows a user to select images from their device and then zip that up with a user defined name (received in the AlertDialog that will not go away immediately).
The things I have tried to get the AlertDialog to go away immediately:
Navigator.of(context).pop()
Navigator.pop(context)
Navigator.of(context, rootNavigator: true).pop()
Tried to make sure the method after it was called asynchronously so it would not block
Added imports in pubspec.yaml
path_provider: ^2.0.2
archive: ^3.1.2
file_picker: ^4.0.3
Code Below:
import 'dart:io';
import 'package:archive/archive_io.dart';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import 'package:path/path.dart';
import 'package:path_provider/path_provider.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 HomePage(title: 'Flutter Demo Home Page'),
);
}
}
class HomePage extends StatefulWidget {
final String title;
const HomePage({required this.title, Key? key}) : super(key: key);
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
late final Directory _uploadsDir;
#override
void initState() {
super.initState();
_init();
}
#override
void dispose() async {
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: ElevatedButton(
onPressed: () => _importFilesSelected(context),
child: const Text("Select Images"),
),
),
);
}
void _init() async {
Directory dir = await getApplicationDocumentsDirectory();
_uploadsDir = await Directory(join(dir.path, "uploads")).create(recursive: true);
}
void _importFilesSelected(BuildContext context) async {
final BuildContext buildContext = context;
FilePickerResult? result = await FilePicker.platform.pickFiles(
dialogTitle: "Select File(s) to Import",
allowMultiple: true,
type: FileType.custom,
allowedExtensions: ['jpg', 'png'],
onFileLoading: (FilePickerStatus status) {
if (status == FilePickerStatus.picking) {
showDialog(
context: buildContext,
barrierDismissible: false,
builder: (BuildContext context) {
return Dialog(
child: Padding(
padding: const EdgeInsets.all(12.0),
child: Row(
mainAxisSize: MainAxisSize.min,
children: const [
CircularProgressIndicator(),
SizedBox(width: 10.0,),
Text("Processing Files..."),
],
),
),
);
},
);
} else {
Navigator.pop(buildContext);
}
},
withData: false
);
_processResult(result, buildContext);
}
void _processResult(FilePickerResult? result, BuildContext buildContext) async {
if (result != null) {
String? zipFilename = await _showGetZipFileNameDialog(buildContext);
print("zipFileName: $zipFilename");
_processImagesIntoZip(result, zipFilename!);
}
}
void _processImagesIntoZip(FilePickerResult result, String zipFilename) async {
String zipPath = join(_uploadsDir.path, zipFilename);
final encoder = ZipFileEncoder();
encoder.create(zipPath);
for (PlatformFile file in result.files) {
encoder.addFile(File(file.path!));
}
}
Future<String?> _showGetZipFileNameDialog(BuildContext context) {
String? zipFilename;
return showDialog<String>(
context: context,
barrierDismissible: false,
builder: (context) {
return AlertDialog(
title: const Text("Save As"),
content: Form(
key: _formKey,
child: TextFormField(
decoration: const InputDecoration(
labelText: "Save As:",
),
validator: (val) {
return val!.isEmpty ? "Please fill out filename" : null;
},
onSaved: (val) => zipFilename = "${val!}.zip",
),
),
actions: [
TextButton(
child: const Text("Cancel"),
onPressed: () {
Navigator.pop(context);
},
),
TextButton(
child: const Text("OK"),
onPressed: () {
if (_formKey.currentState!.validate()) {
_formKey.currentState!.save();
// Remove the Dialog from the screen
Navigator.pop(context, zipFilename);
}
},
),
],
);
}
);
}
}
I had similar issue
Was fixed using
Navigator.pop(context);
Navigator.pop(context) is the right answer, so if it is not working it means something is off.
Did you create 2 Navigators? That happens when u accidentably create a new MaterialApp instead of Scaffold, for example.
Related
I have a function named saveData which is executed on pressing a button. I want if I click on the button I execute saveData function and the value of the button become stop then when I click on stop the function should be fininish.
this is the button code:
Align(
alignment: Alignment.bottomCenter,
child: TextButton(
onPressed: () {
saveData();
},
child: Text('Save Data'),
),
),
One way to achieve what you want is simply to create a flag to control which button (text/action) is shown at any given moment:
TextButton(
onPressed: isSaving ? Finish : saveData,
child: isSaving ? const Text("Stop") : const Text("Save Data"),
)
Try the following working complete sample to see what i mean:
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
#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 StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
bool isSaving = false;
Future saveData() async {
isSaving = true;
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text("Saving data..."),duration: Duration(hours: 1),)
);
setState(() { });
}
void Finish() {
ScaffoldMessenger.of(context).hideCurrentSnackBar();
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text("Saving data stopped..."),duration: Duration(seconds: 1),)
);
isSaving = false;
setState(() { });
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: TextButton(
onPressed: isSaving ? Finish : saveData,
child: isSaving ? const Text("Stop") : const Text("Save Data"),
)
),
);
}
}
This will produce a result like:
State 1
After Save Data is tapped
You need state management.
State Management
This is a way to manage your user interface controls such as text fields, buttons, images, etc. It controls what and when something should display or perform an action. More about Flutter state management here
Codebase Sample
String name = ""; // setting an empty name variable
Align(
alignment: Alignment.bottomCenter,
child: TextButton(
onPressed: () {
setState(() {
name = "new name"; // updating the name variable with setState
});
},
child: Text('Save Data'),
),
),
Now, to implement your idea. You need a bool variable that changes the state on the button click action. To do that, look what I did below
bool isClicked = false;
Align(
alignment: Alignment.bottomCenter,
child: TextButton(
onPressed: () {
setState(() => isClicked = !isClicked); // change click state
if (isClicked) {
// do something on click
} else {
// do something off click
}
},
child: Text(isClicked ? "Stop" : "Save Data"), // if isClicked display "Stop" else display "Save Data"
),
),
Another way to do this is to create two different functions. One for saving user data, and the other of stop and calling the action based on a bool state.
onPressed: isSaving ? saveData : stop,
You can use the method above to update your user data as well if any misunderstand or need future help, comment below. Bye
Basically this is a state management problem.
you get more information about state management from here
Here a code for solve your problem
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.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 HomeView(),
);
}
}
class HomeView extends StatefulWidget {
const HomeView({Key? key}) : super(key: key);
#override
State<HomeView> createState() => _HomeViewState();
}
class _HomeViewState extends State<HomeView> {
bool _savePressed = false;
void _save() {
// TODO do whatever you want
}
void _stop() {
// TODO do whatever you want
}
void _onButtonPressed() {
setState(() {
_savePressed = !_savePressed;
_savePressed ? _save() : _stop();
});
}
String get _getButtonText => _savePressed ? "Stop" : "Save";
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Align(
alignment: Alignment.bottomCenter,
child: TextButton(
onPressed: _onButtonPressed,
child: Text(_getButtonText),
),
),
);
}
}
I have an alert dialog with a switch. If I press the switch, the boolean changes like I want it to (I have it set so the homepage background color changes based on the bool), but the switch animation does not register until I close out of the alert dialog and re-open it again. I tried wrapping it in a StatefulBuilder like I saw in another post, but in that case, the animation works but the bool does not change. I am trying to share this bool value throughout my app, so I'm using Shared Preferences.
here is the full code
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage();
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
bool bulb = false;
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: bulb ? Colors.white : Colors.blue,
appBar: AppBar(actions: <Widget>[
IconButton(
icon: Icon(Icons.settings),
onPressed: () {
showDialog(
context: context,
builder: (ctx) => AlertDialog(
content: Column(children: [
Switch(
value: bulb,
onChanged: (bool isChecked) async {
final prefs = await SharedPreferences.getInstance();
setState(() {
bulb = isChecked;
prefs.setBool('bulb', isChecked);
});
},
),
StatefulBuilder(builder:
(BuildContext context, StateSetter setState) {
return Column(children: [
Switch(
value: bulb,
onChanged: (bool isChecked) async {
final prefs =
await SharedPreferences.getInstance();
setState(() {
bulb = isChecked;
prefs.setBool('bulb', isChecked);
});
},
),
]);
})
])));
})
]),
body: Center(),
);
}
}
Solution:
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage();
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
bool bulb = false;
Future openDialog() => showDialog(
context: context,
builder: (context) => StatefulBuilder(
builder: (context, setState) => AlertDialog(
content: Column(children: [
Switch(
value: bulb,
onChanged: (bool isChecked) async {
final prefs = await SharedPreferences.getInstance();
setState(() {
bulb = isChecked;
prefs.setBool('bulb', isChecked);
});
},
),
ElevatedButton(
onPressed: () => Navigator.of(context).pop(),
child: Text("Save")),
]))));
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: bulb ? Colors.white : Colors.blue,
appBar: AppBar(actions: <Widget>[
IconButton(
icon: Icon(Icons.settings),
onPressed: () async {
await openDialog();
setState(() {});
}),
]),
body: Center(),
);
}
}
You have to use the StatefulBuilder itself.
Now, when you are calling setState inside the dialog, only the state inside the dialog is changing. The state of the screen remains the same. This is why switch changed animation is happening but the bulb value in the screen is not changing.
The workaround is, you can use a callback function to call setState in the screen too, when the value of switch changes.
I am learning how to implement login/register system in my Flutter app with Firebase (using firebase_auth library). It works but not as expected. The first time to sign-in the "FirebaseAuth.instance.signInWithEmailAndPassword" takes a lot of time to resolve. But once signed-in if I sign-out and sign-in again it goes faster (faster I mean a normal speed).
Main.dart
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
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 LoginView());
}
}
LoginView.dart
import 'package:flutter/material.dart';
import 'package:firebase_auth/firebase_auth.dart';
class LoginView extends StatefulWidget {
const LoginView({Key? key}) : super(key: key);
#override
State<LoginView> createState() => _LoginViewState();
}
class _LoginViewState extends State<LoginView> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
child: SizedBox(
child: StreamBuilder<User?>(
stream: FirebaseAuth.instance.authStateChanges(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
style: ElevatedButton.styleFrom(
minimumSize: const Size.fromHeight(50),
),
child: const Text('Sign-out'),
onPressed: () async {
await FirebaseAuth.instance.signOut();
},
),
],
);
}
print(snapshot.data);
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
style: ElevatedButton.styleFrom(
minimumSize: const Size.fromHeight(50),
),
child: const Text('Log In'),
onPressed: () async {
try {
UserCredential userCredential = await FirebaseAuth.instance
.signInWithEmailAndPassword(
email: "test#testt.com", password: "testpass");
print("Logged");
} on FirebaseAuthException catch (e) {
if (e.code == 'weak-password') {
print('The password provided is too weak.');
} else if (e.code == 'email-already-in-use') {
print('The account already exists for that email.');
}
} catch (e) {
print(e);
}
},
),
],
);
},
),
),
));
}
}
What can be the problem?
I'm trying to implement login with Facebook in my app which it's "working" but not necessarily as expected, I'll write down the issues I'm having.
When I try to login, I don't get redirected to the homepage and the logs show an error this error:
W/Firestore(27484): (24.1.2) [Firestore]: Listen for Query(target=Query(users where Email==random_email#gmail.com order by name);limitType=LIMIT_TO_FIRST) failed: Status{code=PERMISSION_DENIED, description=Missing or insufficient permissions., cause=null}
E/flutter (27484): [ERROR:flutter/lib/ui/ui_dart_state.cc(198)] Unhandled Exception: [cloud_firestore/permission-denied] The caller does not have permission to execute the specified operation.
I'm redirected to the login page again and the login only works in the second try.
When I sign out it looks like it works fine because I got redirected to the login page but the logs show the same error above.
For last some times it happens that if I exit the app(by pressing back button) and then re-open I get redirected to the home page instead of the login page it doesn't happen always so not sure what is causing that. I'm displaying the user data in the home page and in this case it's empty.
I was thinking that it could be related to my rules set in firebase but I'm not really sure , I would appreciate it very much if someone could guide me or point me what I'm missing or doing incorrectly.
Below my code:
main.dart
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
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(
primarySwatch: Colors.blue,
),
home: const Wrapper(),
routes: <String, WidgetBuilder>{
'/home': (BuildContext context) => const HomePage(),
'/login': (BuildContext context) => const LoginPage(),
},
);
}
}
wrapper.dart
class Wrapper extends StatelessWidget {
const Wrapper({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
//print(FirebaseAuth.instance.currentUser);
if(FirebaseAuth.instance.currentUser?.email == null){
return const LoginPage();
}
else{
return const HomePage();
}
}
}
login_page.dart
class LoginPage extends StatefulWidget {
const LoginPage({Key? key}) : super(key: key);
String get title => 'login';
#override
State<LoginPage> createState() => _LoginPageState();
}
class _LoginPageState extends State<LoginPage> {
String userEmail = "";
UserModel? _currentUser;
bool loading = false;
#override
Widget build(BuildContext context) {
return WillPopScope(
child: Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Padding(
padding: EdgeInsets.symmetric(horizontal: 8.0, vertical: 10.0),
child: TextField(
decoration: InputDecoration(
border: OutlineInputBorder(),
hintText: 'Email',
),
),
),
const Padding(
padding:
EdgeInsets.symmetric(horizontal: 8.0, vertical: 10.0),
child: TextField(
decoration: InputDecoration(
border: OutlineInputBorder(),
hintText: 'Password',
),
)),
CustomWidgets.socialButtonRect('Login with Facebook',
facebookColor, FontAwesomeIcons.facebookF, onTap: () async {
await signInWithFacebook();
}),
Padding(
padding: const EdgeInsets.all(15.0),
child:
loading ? const CircularProgressIndicator() : Container(),
),
],
),
), // This trailing comma makes auto-formatting nicer for build methods.
),
onWillPop: () async {
final shouldPop = await showDialog<bool>(
context: context,
builder: (context) {
return AlertDialog(
title: const Text('Do you want to go exit?'),
actionsAlignment: MainAxisAlignment.spaceBetween,
actions: [
TextButton(
onPressed: () {
Navigator.pop(context, true);
},
child: const Text('Yes'),
),
TextButton(
onPressed: () {
Navigator.pop(context, false);
},
child: const Text('No'),
),
],
);
},
);
return shouldPop!;
},
);
}
Future<void> signInWithFacebook() async {
final LoginResult loginResult =
await FacebookAuth.instance.login(permissions: ['email']);
// Create a credential from the access token
final OAuthCredential facebookAuthCredential =
FacebookAuthProvider.credential(loginResult.accessToken!.token);
final userData = await FacebookAuth.instance.getUserData();
FirebaseAuth.instance.signInWithCredential(facebookAuthCredential);
var snapshot = await FirebaseFirestore.instance
.collection('users')
.where('Email', isEqualTo: userData['email'])
.get();
if (snapshot.size == 0) {
await FirebaseFirestore.instance
.collection('users')
.add({'Email': userData["email"], 'Name': userData['name']});
}
//userEmail = userData["email"];
UserModel user = UserModel.fromJson(userData);
_currentUser = user;
setState(() {
loading = true;
});
Navigator.pushNamed(context, '/home');
}
}
home_page.dart
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
#override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
final FirebaseAuth auth = FirebaseAuth.instance;
bool loading = false;
String name = "";
#override
Widget build(BuildContext context) {
getName();
return Scaffold(
backgroundColor: Colors.blue,
bottomNavigationBar: const BottomBar(),
body: SafeArea(
child: Column(
children: [
ListView.builder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemCount: 2,
itemBuilder: (context, index) {
return matchTile();
}),
Center(
child: ElevatedButton(
onPressed: () async {
//Navigator.pop(context,true);
//Navigator.popUntil(context, ModalRoute.withName('/login'));
Navigator.of(context).pushReplacementNamed('/login');
await signOut();
},
child: Text(name),
style: TextButton.styleFrom(
primary: Colors.redAccent,
onSurface: Colors.red,
),
),
),
],
),
),
);
}
Future<void> signOut() async {
await FacebookAuth.i.logOut();
await FirebaseAuth.instance.signOut();
}
Future<void> getName() async {
User? user = auth.currentUser;
await user?.reload();
user = auth.currentUser;
String? temp = user?.email;
var snapshot = await FirebaseFirestore.instance
.collection('users')
.where('Email', isEqualTo: temp)
.limit(1)
.get()
.then((value) => value.docs[0]);
Map<String, dynamic> data = snapshot.data();
//print('the lenght : $data.length');
setState(() {
name = data['Name'];
});
}
}
For last these are the rules I have set in firebase
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write: if request.auth != null;
}
}
}
You probably are missing an await.
Without this await the user is not logged when accessing Firestore, as the code "just runs by".
await FirebaseAuth.instance.signInWithCredential(facebookAuthCredential);
I have application which has mappage using location
class _MapPageState extends State<MapPage> {
LocationData currentLocation;
Location _locationService = new Location();
#override
void initState(){
super.initState();
_locationService.onLocationChanged().listen((LocationData result) async {
setState(() {
print(result.latitude);
print(result.longitude);
currentLocation = result;
});
});
}
In this case, setState() works well when mappage is shown.
However after mappage is disposed, there comes error like this.
E/flutter ( 6596): This error happens if you call setState() on a State object for a widget that no longer appears in the widget tree (e.g., whose parent widget no longer includes the widget in its build). This error can occur when code calls setState() from a timer or an animation callback.
E/flutter ( 6596): The preferred solution is to cancel the timer or stop listening to the animation in the dispose() callback. Another solution is to check the "mounted" property of this object before calling setState() to ensure the object is still in the tree.
So, I have two ideas.
Remove onLocationChanged() listener when page is disposed.
Check if State is disposed or not before setState()
How can I solve this??
You can copy paste two files below and directly replace official example's code
https://github.com/Lyokone/flutterlocation/tree/master/location/example/lib
After Navigate to ListenLocationWidget page,
you can call _stopListen() in dispose()
code snippet
class _MyHomePageState
...
RaisedButton(
child: Text('Open route'),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute<void>(
builder: (context) => ListenLocationWidget()),
);
},
),
PermissionStatusWidget(),
Divider(height: 32),
ServiceEnabledWidget(),
Divider(height: 32),
GetLocationWidget(),
Divider(height: 32),
//ListenLocationWidget()
class _ListenLocationState extends State<ListenLocationWidget> {
...
StreamSubscription<LocationData> _locationSubscription;
String _error;
#override
void initState() {
print("initState");
super.initState();
_listenLocation();
}
#override
void dispose() {
print("stopListen");
_stopListen();
super.dispose();
}
Future<void> _listenLocation() async {
_locationSubscription =
location.onLocationChanged.handleError((dynamic err) {
setState(() {
_error = err.code;
});
_locationSubscription.cancel();
}).listen((LocationData currentLocation) {
setState(() {
print("setState");
_error = null;
_location = currentLocation;
});
});
}
Future<void> _stopListen() async {
_locationSubscription.cancel();
}
working demo
full code ListenLocationWidget
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:location/location.dart';
class ListenLocationWidget extends StatefulWidget {
const ListenLocationWidget({Key key}) : super(key: key);
#override
_ListenLocationState createState() => _ListenLocationState();
}
class _ListenLocationState extends State<ListenLocationWidget> {
final Location location = Location();
LocationData _location;
StreamSubscription<LocationData> _locationSubscription;
String _error;
#override
void initState() {
print("initState");
super.initState();
_listenLocation();
}
#override
void dispose() {
print("stopListen");
_stopListen();
super.dispose();
}
Future<void> _listenLocation() async {
_locationSubscription =
location.onLocationChanged.handleError((dynamic err) {
setState(() {
_error = err.code;
});
_locationSubscription.cancel();
}).listen((LocationData currentLocation) {
setState(() {
print("setState");
_error = null;
_location = currentLocation;
});
});
}
Future<void> _stopListen() async {
_locationSubscription.cancel();
}
#override
Widget build(BuildContext context) {
return SafeArea(
child: Container(
color: Colors.white,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
'Listen location: ' + (_error ?? '${_location ?? "unknown"}'),
style: Theme.of(context).textTheme.body2,
),
Row(
children: <Widget>[
Container(
margin: const EdgeInsets.only(right: 42),
child: RaisedButton(
child: const Text('Listen'),
onPressed: _listenLocation,
),
),
RaisedButton(
child: const Text('Stop'),
onPressed: _stopListen,
)
],
),
],
),
),
);
}
}
full code main.dart
import 'package:flutter/material.dart';
import 'package:location/location.dart';
import 'package:url_launcher/url_launcher.dart';
import 'get_location.dart';
import 'listen_location.dart';
import 'permission_status.dart';
import 'service_enabled.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Location',
theme: ThemeData(
primarySwatch: Colors.amber,
),
home: const MyHomePage(title: 'Flutter Location Demo'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final Location location = Location();
Future<void> _showInfoDialog() {
return showDialog<void>(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: const Text('Demo Application'),
content: SingleChildScrollView(
child: ListBody(
children: <Widget>[
const Text('Created by Guillaume Bernos'),
InkWell(
child: Text(
'https://github.com/Lyokone/flutterlocation',
style: TextStyle(
decoration: TextDecoration.underline,
),
),
onTap: () =>
launch('https://github.com/Lyokone/flutterlocation'),
),
],
),
),
actions: <Widget>[
FlatButton(
child: const Text('Ok'),
onPressed: () {
Navigator.of(context).pop();
},
),
],
);
},
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
actions: <Widget>[
IconButton(
icon: Icon(Icons.info_outline),
onPressed: _showInfoDialog,
)
],
),
body: Container(
padding: const EdgeInsets.all(32),
child: Column(
children: <Widget>[
RaisedButton(
child: Text('Open route'),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute<void>(
builder: (context) => ListenLocationWidget()),
);
},
),
PermissionStatusWidget(),
Divider(height: 32),
ServiceEnabledWidget(),
Divider(height: 32),
GetLocationWidget(),
Divider(height: 32),
//ListenLocationWidget()
],
),
),
);
}
}