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()
],
),
),
);
}
}
Related
I am making a chat app using flutter. It is loading data from firestore but when I am using annimation in the drawer the chat's aren't loading I don't know the reason if i comment out the animation in the chat screen it works and it works also if comment out if block which contain connection state.waiting without commenting annimation
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:my_flutter_app/chat/messages.dart';
import 'package:my_flutter_app/chat/new_message.dart';
class ChatScreen extends StatefulWidget {
const ChatScreen({Key? key}) : super(key: key);
#override
State<ChatScreen> createState() => _ChatScreenState();
}
class _ChatScreenState extends State<ChatScreen> with SingleTickerProviderStateMixin{
late AnimationController controller;
late Animation animation;
#override
void initState() {
super.initState();
controller=AnimationController(
duration: Duration(seconds: 2),
vsync: this);
animation=CurvedAnimation(parent: controller, curve: Curves.decelerate);
controller.forward();
animation.addStatusListener((status) {
if(status==AnimationStatus.completed){
controller.reverse(from: 1.0);
}else if(status==AnimationStatus.dismissed){
controller.forward();
}
});
controller.addListener(() {
setState(() {
animation.value;
});
});
}
#override
void dispose() {
super.dispose();
controller.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
drawer: Drawer(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Container(
margin: EdgeInsets.only(top: 50),
padding: EdgeInsets.all(15),
child: CircleAvatar(backgroundImage: AssetImage('assets/profile.png'),radius: 60),),
Container(
child: Image.asset('assets/welcome.png'),
height: animation.value*100,
),
InkWell(
onTap: (){
showCupertinoDialog(context: context, builder: (context)=>CupertinoAlertDialog(
title: Text('Alert'),
content: Text('Are you sure you want to logout?'),
actions: [
CupertinoDialogAction(child: Text('Yes'),onPressed: (){
FirebaseAuth.instance.signOut();
Navigator.pop(context);
},),
CupertinoDialogAction(child: Text('No'),onPressed: (){
Navigator.pop(context);
},)
],
));
//FirebaseAuth.instance.signOut();
},
child: ListTile(leading: Icon(Icons.logout,color: Colors.white),
title: Text('LOGOUT',style: TextStyle(color: Colors.white,fontWeight: FontWeight.bold),),
tileColor: Colors.red,),
)
],
),
),
appBar: AppBar(title: Text('Chats'),centerTitle: true,backgroundColor: Colors.green),
body: Container(
child: Column(
children: [
Expanded(child: Messages()),
NewMessage(),
],
),
),
);
}
}
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:my_flutter_app/chat/mesui.dart';
class Messages extends StatefulWidget {
const Messages({Key? key}) : super(key: key);
#override
State<Messages> createState() => _MessagesState();
}
class _MessagesState extends State<Messages> {
final auth=FirebaseAuth.instance;
User ?cuser;
#override
void initState() {
// TODO: implement initState
super.initState();
getCurrentUser();
}
void getCurrentUser(){
try {
final user = auth.currentUser;
if (user != null) {
cuser = user;
}
}
catch(e){
print(e);
}
}
#override
Widget build(BuildContext context) {
return StreamBuilder(stream: FirebaseFirestore.instance.collection('chat').orderBy('messaged_on',descending: true).snapshots(),
builder: (context,snaps) {
if(snaps.connectionState == ConnectionState.waiting){
return Center(
child: CircularProgressIndicator(),
);
}
final documents=snaps.data!.docs;
final nowperson=cuser!.email;
return ListView.builder(
reverse: true,
itemBuilder: (ctx,index){
final sender=documents[index]['email'];
bool isMe= sender==nowperson;
return Mesui(documents[index]['text'],isMe);
},
itemCount: documents.length,);
},
);
}
}
How am I supposed to pass a value in this big mess called Flutter?
30 years old php global $var wasn't good?
All these years were to come up with setState, passed in a controller which get redeclared as a key inside a stateful widget that receive the value from a Navigator?
By the way, I tried using Navigator.push but it seems to open a completely new window, the value is there but I'd need it to show in the tab body not in a new window, below is my code:
main.dart
import 'dart:core';
import 'dart:developer';
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:qr_code_scanner/qr_code_scanner.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter App',
theme: ThemeData(
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: HomeView(),
);
}
}
class HomeView extends StatefulWidget {
#override
_HomeViewState createState() => _HomeViewState();
}
class _HomeViewState extends State<HomeView> {
final tabs = [QRViewExample(), SecondView(res: '')];
int _currentIndex = 0;
#override
void initState() {
setState(() {});
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
toolbarHeight: 40.0,
elevation: 0,
centerTitle: true,
title: Text('Flutter App'),
),
body: tabs[_currentIndex],
bottomNavigationBar: BottomNavigationBar(
backgroundColor: Colors.red,
currentIndex: _currentIndex,
type: BottomNavigationBarType.fixed,
selectedItemColor: Colors.white,
unselectedItemColor: Colors.white.withOpacity(0.5),
items: [
BottomNavigationBarItem(
icon: Icon(Icons.qr_code),
label: 'Scan',
),
BottomNavigationBarItem(
icon: Icon(Icons.list),
label: 'List',
),
],
onTap: (index) {
setState(() {
_currentIndex = index;
});
},
),
);
}
}
// SECOND TAB WIDGET (custom)
class SecondView extends StatelessWidget {
const SecondView({Key? key, required this.res}) : super(key: key);
final String? res;
#override
Widget build(BuildContext context) {
return Container(
child: Center(
child: Text(res!),
),
);
}
}
// FIRST TAB WIDGET (qrcode)
class QRViewExample extends StatefulWidget {
const QRViewExample({Key? key}) : super(key: key);
#override
State<StatefulWidget> createState() => _QRViewExampleState();
}
class _QRViewExampleState extends State<QRViewExample> {
Barcode? result;
QRViewController? controller;
final GlobalKey qrKey = GlobalKey(debugLabel: 'QR');
#override
void reassemble() {
super.reassemble();
if (Platform.isAndroid) {
controller!.pauseCamera();
}
controller!.resumeCamera();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
height: 500,
child: Padding(
padding: EdgeInsets.all(8.0),
child: Column(
children: <Widget>[
Expanded(flex: 4, child: _buildQrView(context)),
Expanded(
flex: 1,
child: FittedBox(
fit: BoxFit.contain,
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
if (result != null)
Text(
'Barcode Type: ${describeEnum(result!.format)} Data: ${result!.code}')
else
const Text('Scan a code'),
Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Container(
margin: const EdgeInsets.all(8),
child: ElevatedButton(
onPressed: () async {
await controller?.toggleFlash();
setState(() {});
},
child: FutureBuilder(
future: controller?.getFlashStatus(),
builder: (context, snapshot) {
return Text('Flash: ${snapshot.data}');
},
)),
),
Container(
margin: const EdgeInsets.all(8),
child: ElevatedButton(
onPressed: () async {
await controller?.flipCamera();
setState(() {});
},
child: FutureBuilder(
future: controller?.getCameraInfo(),
builder: (context, snapshot) {
if (snapshot.data != null) {
return Text(
'Camera facing ${describeEnum(snapshot.data!)}');
} else {
return const Text('loading');
}
},
)),
)
],
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Container(
margin: const EdgeInsets.all(8),
child: ElevatedButton(
onPressed: () async {
await controller?.pauseCamera();
},
child: const Text('pause',
style: TextStyle(fontSize: 20)),
),
),
Container(
margin: const EdgeInsets.all(8),
child: ElevatedButton(
onPressed: () async {
await controller?.resumeCamera();
},
child: const Text('resume',
style: TextStyle(fontSize: 20)),
),
)
],
),
],
),
),
)
],
),
),
),
);
}
Widget _buildQrView(BuildContext context) {
var scanArea = (MediaQuery.of(context).size.width < 400 ||
MediaQuery.of(context).size.height < 400)
? 150.0
: 300.0;
return QRView(
key: qrKey,
onQRViewCreated: _onQRViewCreated,
overlay: QrScannerOverlayShape(
borderColor: Colors.cyanAccent,
borderRadius: 10,
borderLength: 30,
borderWidth: 10,
cutOutSize: scanArea),
onPermissionSet: (ctrl, p) => _onPermissionSet(context, ctrl, p),
);
}
void _onQRViewCreated(QRViewController controller) {
setState(() {
this.controller = controller;
});
controller.scannedDataStream.listen((scanData) {
controller.pauseCamera();
setState(() {
result = scanData;
});
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SecondView(res: result!.code)))
.then((value) => controller.resumeCamera());
});
}
void _onPermissionSet(BuildContext context, QRViewController ctrl, bool p) {
log('${DateTime.now().toIso8601String()}_onPermissionSet $p');
if (!p) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('no Permission')),
);
}
}
#override
void dispose() {
controller?.dispose();
super.dispose();
}
}
How am I supposed to pass a value in this big mess called Flutter?
With state management tools like InheritedWidget, InheritedModel, Provider, BloC and many more.
30 years old php global $var wasn't good? All these years were to come up with setState, passed in a controller which get redeclared as a key inside a stateful widget that receive the value from a Navigator?
Well, you shouldn't do that and it's not meant to be done like that. We can use several methods to propagate data down the widget tree. Let me explain this with InheritedWidget. But sometimes you want to go for Provider which is a wrapper class for InheritedWidget.
First we create a class named QRListModel which extends InheritedModel:
class QRListModel extends InheritedWidget {
final List<Barcode> qrList = []; // <- This holds our data
QRListModel({required super.child});
#override
bool updateShouldNotify(QRListModel oldWidget) {
return !listEquals(oldWidget.qrList, qrList);
}
static QRListModel of(BuildContext context) {
final QRListModel? result = context.dependOnInheritedWidgetOfExactType<QRListModel>();
assert(result != null, 'No QRListModel found in context');
return result!;
}
}
updateShouldNotify is a method we have to override to tell Flutter, when we want the widgets to rebuild. We want this to happen when the list changes. The of method is just a handy way to access the QRListModel.
Now wrap a parent widget of both the scan tab view and the list tab view inside QRListModel. We go for HomeView:
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter App',
theme: ThemeData(
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: QRListModel(child: HomeView()), // <- here!
);
}
}
We can take any parent widget but it should be a class where we don't call setState. Otherwise our QRListModel also gets rebuilt and our list is gone.
Now we can access QRListModel from anywhere inside the subtree. We need it here:
void _onQRViewCreated(QRViewController controller) {
setState(() {
this.controller = controller;
this.controller!.resumeCamera();
});
controller.scannedDataStream.listen((scanData) async {
controller.pauseCamera();
QRListModel.of(context).qrList.add(scanData); // <- Here we access the list
await showDialog(
context: context,
builder: (context) => SimpleDialog(
title: Text("Barcode was added!"),
children: [
Text(scanData.code!)
],
)
);
});
}
And here we read the list:
class SecondView extends StatelessWidget {
const SecondView({Key? key, required this.res}) : super(key: key);
final String? res;
#override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: QRListModel.of(context).qrList.length,
itemBuilder: (context, index) {
return Card(
child: ListTile(
title: Text(QRListModel.of(context).qrList[index].code ?? "NO"),
),
);
}
);
}
}
Now both pages have access to the qr list. Please do mind that a InheritedWidget can only have final fields. So if you need mutable fields, you need an additional wrapper class. We don't need it as we don't change the list but only its elements.
By the way: You shouldn't call setState inside initState. You did this here:
class _HomeViewState extends State<HomeView> {
final tabs = [QRViewExample(), SecondView(res: '')];
int _currentIndex = 0;
#override
void initState() {
setState(() {}); // <- Don't call setState inside initState!
super.initState();
}
any tips or help how can I make this on tap moveable list in flutter?
https://files.fm/f/txdn29dg3
The provided component is exactly what CupertinoPicker could offer you.
Also, as suggested in the documentation, you should combine the CupertinoPicker with showCupertinoModalPopup to display the picker modally at the bottom of the screen.
This is how the code could look like:
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark().copyWith(
scaffoldBackgroundColor: darkBlue,
),
debugShowCheckedModeBanner: false,
home: const Scaffold(
body: Center(
child: PickerPage(),
),
),
);
}
}
class PickerPage extends StatefulWidget {
const PickerPage();
#override
_PickerPageState createState() => _PickerPageState();
}
class _PickerPageState extends State<PickerPage> {
final _items = [
'Flat Rate',
'Hourly',
'Request for Price',
];
int _selectedItem = 0;
void _onSelectedItemChanged(int value) => setState(
() => _selectedItem = value,
);
void _showPicker() {
showCupertinoModalPopup(
context: context,
builder: (_) => PickerExample(
items: _items,
selectedItem: _selectedItem,
onSelectedItemChanged: _onSelectedItemChanged,
),
);
}
#override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(_items[_selectedItem]),
const SizedBox(height: 10.0),
ElevatedButton(
child: const Text('Show picker'),
onPressed: _showPicker,
),
],
);
}
}
class PickerExample extends StatefulWidget {
final List<String> items;
final int selectedItem;
final ValueSetter<int> onSelectedItemChanged;
const PickerExample({
required this.items,
required this.selectedItem,
required this.onSelectedItemChanged,
});
#override
_PickerExampleState createState() => _PickerExampleState();
}
class _PickerExampleState extends State<PickerExample> {
late final FixedExtentScrollController _controller;
#override
void initState() {
super.initState();
_controller = FixedExtentScrollController(initialItem: widget.selectedItem);
}
#override
void dispose() {
_controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return SizedBox(
height: 300,
child: CupertinoPicker(
scrollController: _controller,
backgroundColor: Colors.white,
itemExtent: 30.0,
children: [
for (final item in widget.items) Center(child: Text(item)),
],
onSelectedItemChanged: widget.onSelectedItemChanged,
),
);
}
}
You could also find an interactive example in this DartPad.
How to turn On/Off vibration on Homepage from Settings page with boolean SwitchListTile?
I want if the SwitchListTile in the Settings page is On, the Homepage will vibrate every time I tap it, and vice versa. basically I don't know how to control certain pages from other pages
this is MySettingPage
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
class MySettingPage extends StatefulWidget {
const MySettingPage({Key key}) : super(key: key);
#override
_MySettingPageState createState() => _MySettingPageState();
}
class _MySettingPageState extends State<MySettingPage> {
bool isVibrate = false;
#override
void initState() {
super.initState();
getSwitchValues();
}
getSwitchValues() async {
isVibrate = await getSwitchState();
setState(() {});
}
Future<bool> saveSwitchState(bool value) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.setBool("switchState", value);
return prefs.setBool("switchState", value);
}
Future<bool> getSwitchState() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
bool isVibrate = prefs.getBool("switchState");
return isVibrate;
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
titleSpacing: 0,
title: Text("Pengaturan"),
leading: IconButton(
icon: Icon(Icons.arrow_back),
onPressed: () {
Navigator.of(context).pop();
}),
),
body: Container(
padding: EdgeInsets.all(10),
child: ListView(
children: [
SwitchListTile(
title: Text("Getar"),
value: isVibrate,
onChanged: (bool value) async {
setState(() {
isVibrate = value;
saveSwitchState(value);
});
},
),
//
],
),
),
);
}
}
this is MyHomePage
import 'package:flutter/material.dart';
import 'package:vibration/vibration.dart';
import 'mysettingpage.dart';
class MyHomePage extends StatefulWidget {
final bool isVibrate;
MyHomePage({Key key, this.isVibrate}) : super(key: key);
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
if (widget.isVibrate == true) {
Vibration.vibrate(duration: 70);
}
if (widget.isVibrate == false) {
Vibration.cancel();
}
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("My Homepage"),
titleSpacing: 0,
leading: IconButton(
icon: Icon(Icons.settings),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (BuildContext context) => MySettingPage(),
));
},
),
),
body: GestureDetector(
onTap: () {
_incrementCounter();
},
child: Container(
height: double.infinity,
width: double.infinity,
child: Padding(
padding: const EdgeInsets.only(bottom: 120),
child: Column(
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Padding(
padding: EdgeInsets.symmetric(horizontal: 20),
child: FittedBox(
child: Text(
'$_counter',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 200,
fontFamily: 'DS-Digital',
color: Color(0xFF24F3E2),
),
),
),
),
],
),
),
],
),
),
),
),
);
}
}
To continue on the response from Allan C with the changes to your code: (untested)
HomePage:
import 'package:flutter/material.dart';
import 'package:vibration/vibration.dart';
import 'mysettingpage.dart';
class MyHomePage extends StatefulWidget {
final bool isVibrate;
MyHomePage({Key key, this.isVibrate}) : super(key: key);
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
bool _isVibrate;
#override
void initState() {
super.initState();
_isVibrate = widget.isVibrate;
}
void _onVibrateChange(bool value) {
setState(() {
_isVibrate = value;
})
}
void _incrementCounter() {
setState(() {
_counter++;
if (_isVibrate) {
Vibration.vibrate(duration: 70);
}
if (_isVibrate) {
Vibration.cancel();
}
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("My Homepage"),
titleSpacing: 0,
leading: IconButton(
icon: Icon(Icons.settings),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (BuildContext context) => MySettingPage(
onChange: _onVibrateChange
),
));
},
),
),
body: GestureDetector(
onTap: () {
_incrementCounter();
},
child: Container(
height: double.infinity,
width: double.infinity,
child: Padding(
padding: const EdgeInsets.only(bottom: 120),
child: Column(
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Padding(
padding: EdgeInsets.symmetric(horizontal: 20),
child: FittedBox(
child: Text(
'$_counter',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 200,
fontFamily: 'DS-Digital',
color: Color(0xFF24F3E2),
),
),
),
),
],
),
),
],
),
),
),
),
);
}
}
using the initState() you set a default value of _isVibrate from the passed value from the widget.isVibrate.
The method _onVibrateChange(bool value) (as a callback) will update the local variable within the state. This method needs to be passed to the MySettingsPage also.
MySettingsPage:
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
class MySettingPage extends StatefulWidget {
const MySettingPage({Key key, this.onChange}) : super(key: key);
final Function(bool value) onChange;
#override
_MySettingPageState createState() => _MySettingPageState();
}
class _MySettingPageState extends State<MySettingPage> {
bool isVibrate = false;
#override
void initState() {
super.initState();
getSwitchValues();
}
getSwitchValues() async {
isVibrate = await getSwitchState();
setState(() {});
}
Future<bool> saveSwitchState(bool value) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.setBool("switchState", value);
widget.onChange(value);
return prefs.setBool("switchState", value);
}
Future<bool> getSwitchState() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
bool isVibrate = prefs.getBool("switchState");
return isVibrate;
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
titleSpacing: 0,
title: Text("Pengaturan"),
leading: IconButton(
icon: Icon(Icons.arrow_back),
onPressed: () {
Navigator.of(context).pop();
}),
),
body: Container(
padding: EdgeInsets.all(10),
child: ListView(
children: [
SwitchListTile(
title: Text("Getar"),
value: isVibrate,
onChanged: (bool value) async {
setState(() {
isVibrate = value;
saveSwitchState(value);
});
},
),
//
],
),
),
);
}
}
I have included a new variable passed to the Statefulwidget (Function(bool value) onChange), this will be the callback for when the switch changes it's value.
In the method Future saveSwitchState(bool value) async there is a call to the passed callback with the updated value from the SwitchListTiles onChange method.
Hope this clarifies what he meant in his answer.
One way to do this is to use a callback function.
So in MySettingPage(), add a constructor such as below:
MySettingPage({this.callback})
final void Function(bool) callback;
In MySettingPage, if you want to update the value of isVibrate in MyHomePage(), you can call widget.callback(true);
In MyHomePage, you can create a method to update the isVibrate variable.
void _updateIsVibrate(bool isVibrate){//...}
When you call MySettingsPage, you can pass in the method you created.
All my problems related to booleans above have been resolved by implementing MultiProvider. Thanks to the above masters who have helped me. have a nice day
I have a main.dart and has a button in center. When user tabs the button it navigate into home.dart page. My home.dart page also has a button on center and when user tabs the button it navigate to details page. The app tree and code is shown below.
I try to implement the "InheritedWidget" in my home.dart so after the home.dart as deep as I go I can call the "void _handleUserInteraction" function using "InheritedWidget". Unluckly I am keep getting error that says:
I/flutter (20715): The getter 'handleOnTap' was called on null.
I/flutter (20715): Receiver: null
I/flutter (20715): Tried calling: handleOnTap
home.dart code:
import 'package:flutter/material.dart';
import 'dart:async';
import 'main.dart';
import 'details.dart';
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
Timer timer;
// TODO: 1 - INIT STATE
#override
void initState() {
super.initState();
setState(() {
_initializeTimer();
});
}
// TODO: 3 - INITIALIZE TIMER
void _initializeTimer() {
timer = Timer.periodic(const Duration(minutes: 5), (__) {
_logOutUser();
});
}
// TODO: 4 - LOG OUT USER
void _logOutUser() {
timer.cancel();
Navigator.push(
context, new MaterialPageRoute(builder: (context) => new MyApp()));
}
// TODO: 5 - HANDLE USER INTERACTION
// void _handleUserInteraction([_]) {
void _handleUserInteraction() {
print("+++++++ _handleUserInteraction Header ++++++++");
if (!timer.isActive) {
return;
}
timer.cancel();
_initializeTimer();
print("+++++++ _handleUserInteraction Footer ++++++++");
}
#override
Widget build(BuildContext context) => MaterialApp(
theme: ThemeData(
primarySwatch: Colors.red,
),
home: LoginState(
callback: _handleUserInteraction,
child: Builder(builder: homeScreenBuilder)),
);
}
#override
Widget homeScreenBuilder(BuildContext context) {
Function() _callback = LoginState.of(context).callback;
return GestureDetector(
onTap: _callback,
onDoubleTap: _callback,
onLongPress: _callback,
onTapCancel: _callback,
child: new Scaffold(
appBar: AppBar(
title: Text("HOME PAGE"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'GOTO DETAILS PAGE',
),
new RaisedButton(
child: new Text("Details"),
onPressed: () {
Navigator.push(
context,
new MaterialPageRoute(
builder: (context) => new Details()));
})
],
),
),
));
}
class LoginState extends InheritedWidget {
final Widget child;
final Function() callback;
final Key key;
LoginState({#required this.callback, #required this.child, this.key})
: super(key: key);
#override
bool updateShouldNotify(LoginState oldWidget) {
return true;
}
static LoginState of(BuildContext context) =>
context.inheritFromWidgetOfExactType(LoginState);
}
details.dart code:
import 'package:flutter/material.dart';
import 'home.dart';
class Details extends StatefulWidget {
#override
_DetailsState createState() => _DetailsState();
}
class _DetailsState extends State<Details> {
#override
Widget build(BuildContext context) {
Function() _callback = LoginState.of(context).callback;
return GestureDetector(
onTap: _callback,
onDoubleTap: _callback,
onLongPress: _callback,
onTapCancel: _callback,
child: new Scaffold(
appBar: AppBar(
title: Text("Details PAGE"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'Every time Tabed it reset the home timer',
),
],
),
),
));
}
}
UPDATE:
I change my home.dart code. The onTap: _callback is working but in details.dart I get same error saying that:
error: - The getter 'callback' was called on null.
The reason why you're getting the error The getter 'callback' was called on null. is because LoginState.of(context) is null.
class _DetailsState extends State<Details> {
#override
Widget build(BuildContext context) {
Function() _callback = LoginState.of(context).callback;
...
}
}
Since you're using InheritedWidget, I assume that you may be attempting to do state management. If so, you can check this guide on implementing app state management. One way of doing this is with the use of provider.
You can try running the sample below. I've based it from the given sample.
main.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'dart:async';
import 'details.dart';
void main() {
// https://flutter.dev/docs/development/data-and-backend/state-mgmt/simple#changenotifierprovider
runApp(ChangeNotifierProvider(
create: (context) => LoginState(),
child: 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.blue,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
void initState() {
super.initState();
// This allows access to LoginState data if no UI changes needed
// https://flutter.dev/docs/development/data-and-backend/state-mgmt/simple#providerof
Provider.of<LoginState>(context, listen: false).initializeTimer();
}
#override
Widget build(BuildContext context) {
// Consumer grants access to LoginState
// https://flutter.dev/docs/development/data-and-backend/state-mgmt/simple#consumer
return Consumer<LoginState>(
builder: (context, loginState, child) {
return GestureDetector(
onTap: () => loginState.handleUserInteraction(),
// onDoubleTap: _callback,
// onLongPress: _callback,
// onTapCancel: _callback,
child: new Scaffold(
appBar: AppBar(
title: Text("HOME PAGE"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'GOTO DETAILS PAGE',
),
new RaisedButton(
child: new Text("Details"),
onPressed: () {
Navigator.push(
context,
new MaterialPageRoute(
builder: (context) => new Details()));
})
],
),
),
));
},
);
}
}
// https://flutter.dev/docs/development/data-and-backend/state-mgmt/simple#changenotifier
class LoginState extends ChangeNotifier {
Timer _timer;
void initializeTimer() {
_timer = Timer.periodic(const Duration(minutes: 5), (__) {
logOutUser();
});
}
void logOutUser() {
_timer.cancel();
}
void handleUserInteraction() {
print("+++++++ _handleUserInteraction Header ++++++++");
if (!_timer.isActive) {
return;
}
_timer.cancel();
initializeTimer();
print("+++++++ _handleUserInteraction Footer ++++++++");
}
}
details.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'main.dart';
class Details extends StatefulWidget {
#override
_DetailsState createState() => _DetailsState();
}
class _DetailsState extends State<Details> {
#override
Widget build(BuildContext context) {
return Consumer<LoginState>(
builder: (context, loginState, child) {
return GestureDetector(
onTap: () => loginState.handleUserInteraction(),
// onDoubleTap: _callback,
// onLongPress: _callback,
// onTapCancel: _callback,
child: new Scaffold(
appBar: AppBar(
title: Text("Details PAGE"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'Every time Tabed it reset the home timer',
),
],
),
),
));
},
);
}
}