How to save max duration with sharedpref in flutter? - flutter

I tried to save duration with sharedpreference only if my last duration is superior to the current duration and so show the MAX duration. my problem is that I don't know how to compare two string duration.
here is the code thanks (#ZeRj)
load_lastPressString()async{
SharedPreferences prefs = await SharedPreferences.getInstance();
setState(() {
final lastPressString = prefs.getString("lastButtonPress");
_lastButtonPress = lastPressString!=null ? DateTime.parse(lastPressString) : DateTime.now();
_updateTimer();
_ticker = Timer.periodic(Duration(seconds:1),(_)=>_updateTimer());
});
}
save_lastPressString()async{
SharedPreferences prefs = await SharedPreferences.getInstance();
setState(() {
prefs.setString('_lastButtonPress', _lastButtonPress.toIso8601String());
});
}
void _updateTimer() {
final duration = DateTime.now().difference(_lastButtonPress);
final newDuration = _formatDuration(duration);
setState(() {
_pressDuration = newDuration;
});
}
String _formatDuration(Duration duration) {
String twoDigits(int n) {
if (n >= 10) return "$n";
return "0$n";
}
String twoDigitMinutes = twoDigits(duration.inMinutes.remainder(60));
String twoDigitSeconds = twoDigits(duration.inSeconds.remainder(60));
return "${twoDigits(duration.inDays)}:${twoDigits(duration.inHours)}:$twoDigitMinutes:$twoDigitSeconds";
}
I tried the code bellow, but I can't use > with string and tried to parse int but not possible with date, and I tried to extract each decimal with regex but it's to complicated and I have errors I havn't understand.
save_max_lastPress()async{
SharedPreferences prefs = await SharedPreferences.getInstance();
setState(() {
if(_lastButtonPress>Max_lastButtonPress)
{
prefs.setString('_max_lastPress', Max_lastButtonPress) ;
}
}
);
}

For that you have to save the maximal as the duration.
Use prefs.setInt("maxDuration",maxDuration.toSeconds()) to save it as a int in shared preferences and load it with
Duration(seconds: prefs.getInt("maxDuration")
You can simply compare two instances of Duration.
I edited the last example to implement this feature:
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutterfly/SharedPrefs.dart';
import 'package:flutterfly/SharedPrefs.dart' as prefix0;
class TestWidget extends StatefulWidget {
#override
_TestWidgetState createState() => _TestWidgetState();
}
class _TestWidgetState extends State<TestWidget> {
DateTime _lastButtonPress;
String _pressDuration;
Timer _ticker;
Duration _maxDuration;
#override
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text("Time since button pressed"),
Text(_pressDuration),
Text("Maximal Duration"),
Text(_formatDuration(_maxDuration)),
RaisedButton(
child: Text("Press me"),
onPressed: () {
_lastButtonPress = DateTime.now();
_updateTimer();
sharedPreferences.setString("lastButtonPress",_lastButtonPress.toIso8601String());
},
)
],
),
);
}
#override
void initState() {
super.initState();
//load max duration, if there is none start with 0
_maxDuration = Duration(seconds:sharedPreferences.getInt("maxDuration")??0);
final lastPressString = sharedPreferences.getString("lastButtonPress");
_lastButtonPress = lastPressString!=null ? DateTime.parse(lastPressString) : DateTime.now();
_updateTimer();
_ticker = Timer.periodic(Duration(seconds:1),(_)=>_updateTimer());
}
#override
void dispose() {
_ticker.cancel();
super.dispose();
}
void _updateTimer() {
final duration = DateTime.now().difference(_lastButtonPress);
//check for new max duration here
Duration newMaxDuration = _maxDuration;
if(duration> _maxDuration) {
//save when current duration is a new max
newMaxDuration = duration;
sharedPreferences.setInt("maxDuration",newMaxDuration.inSeconds);
}
final newDuration =_formatDuration(duration);
setState(() {
_maxDuration = newMaxDuration;
_pressDuration = newDuration;
});
}
String _formatDuration(Duration duration) {
String twoDigits(int n) {
if (n >= 10) return "$n";
return "0$n";
}
String twoDigitMinutes = twoDigits(duration.inMinutes.remainder(60));
String twoDigitSeconds = twoDigits(duration.inSeconds.remainder(60));
return "${twoDigits(duration.inHours)}:$twoDigitMinutes:$twoDigitSeconds";
}
}

Related

Flutter - Check the content of a Json before saving it locally and properly

Please i need some help and thanks in advance.
I am reciving over Websocket a Json that i want to safe locally.
But before saving it, i am trying to compare the recieved Json with the existend locad Json. If the value of count_json does not exist inside the local file it would need to save the Json to a new line and if the value of count_json would already exist it would do nothing.
At this moment, i am able to save it localy, and write it down to a new line in the file.
But i have two problems that i do not know how to solve it.
How i am making the comparising is not good. Because it is saving the recieved Json to a new line even the value of count_jsonalready exist, like as follow.
{"count_json":1,"range_json":[5.5,8.9,7.5,6.7,8.7],"force_json":[5.4,5.3,5.2,5.2,5.1]}
{"count_json":1,"range_json":[9.5,8.3,12.4,13.1,8.5],"force_json":[4.9,4.8,4.8,4.9,5]}
{"count_json":1,"range_json":[11.7,9.7,9.9,11.8,10.2],"force_json":[4.9,5,5.2,5.3,5.5]}
{"count_json":2,"range_json":[19.6,19.6,19.6,19.6,19.6],"force_json":[10,10,10,10,10]}
{"count_json":2,"range_json":[19.4,19.6,19.6,19.6,19.6],"force_json":[9.9,10,10,10,10]}
{"count_json":2,"range_json":[19.4,19.6,19.6,19.6,19.6],"force_json":[9.9,10,10,10,10]}
{"count_json":2,"range_json":[19.4,19.7,19.6,19.6,19.6],"force_json":[9.9,10,10,10,10]}
.
.
But i am expenting this
{"count_json":1,"range_json":[5.5,8.9,7.5,6.7,8.7],"force_json":[5.4,5.3,5.2,5.2,5.1]}
{"count_json":2,"range_json":[19.4,19.7,19.6,19.6,19.6],"force_json":[9.9,10,10,10,10]}
.
.
My approach on how to compara and safe is as follow.
Map<String, dynamic> jsondat = json.decode(message);
String data = json.encode(jsondat);
setState(() {
if (data.contains("count_json")) {
istcycles = jsondat['count_json']; //cycles value
connectedS1Status = true;
if (_myjson['count_json'] != 0) {
_filePath.writeAsString('$data\n', mode: FileMode.append);
}
}
});
If i open the local Json file in Visual Studio code it give me the error message
End of file Expected
That means the stuctur of how i a writing and saving to the Json file is not properly. See above how the structure is inside the file.
Follow the complete code.
//https://docs.flutter.dev/cookbook/persistence/reading-writing-files
// ignore_for_file: avoid_print
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:path_provider/path_provider.dart';
import 'package:web_socket_channel/io.dart';
import 'dart:io';
import 'dart:async';
const String fileName = 'myJsonFile.json';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return const MaterialApp(home: HomePage());
}
}
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
#override
// ignore: library_private_types_in_public_api
_HomePageState createState() {
return _HomePageState();
}
}
class _HomePageState extends State<HomePage> {
late IOWebSocketChannel channel;
late bool
connectedS1Status; //boolean value to track if WebSocket is connected
late int istcycles; //variable for istcycles
late double istforcesensor;
late double istrangesensor;
#override
void initState() {
connectedS1Status =
false; //initially connection status is "NO" so its FALSE
Future.delayed(Duration.zero, () async {
channelconnect(); //connect to WebSocket wth NodeMCU
});
// Instantiate _controllerKey and _controllerValue
print('0. Initialized _myjson: $_myjson');
_readJson();
istcycles = 0; //initial value of istcycles
istforcesensor = 0;
istrangesensor = 0;
super.initState();
}
channelconnect() {
try {
channel = IOWebSocketChannel.connect(
"ws://192.168.1.100:80"); //channel IP : Port
channel.stream.listen(
(message) {
//print(message);
Map<String, dynamic> jsondat = json.decode(message);
String data = json.encode(jsondat);
setState(() {
if (data.contains("count_json")) {
istcycles = jsondat['count_json']; //cycles value
connectedS1Status = true;
if (_myjson['count_json'] != 0) {
_filePath.writeAsString('$data\n', mode: FileMode.append);
}
}
});
},
onDone: () {
print("Web socket is closed");
setState(() {
connectedS1Status = false;
});
},
onError: (error) {
print(error.toString());
},
);
} catch (_) {
print("error on connecting to websocket.");
}
}
bool _fileExists = false;
late File _filePath;
// First initialization of _json (if there is no json in the file)
late Map<String, dynamic> _myjson = {};
late String _myjsonString;
Future<String> get _localPath async {
final directory = await getApplicationDocumentsDirectory();
return directory.path;
}
Future<File> get _localFile async {
final path = await _localPath;
return File('$path/$fileName');
}
//------------------------------------------------------------------------------------
// _readJson--------------------------------------------------------------------------
//------------------------------------------------------------------------------------
void _readJson() async {
// Initialize _filePath
_filePath = await _localFile;
// 0. Check whether the _file exists
_fileExists = await _filePath.exists();
print('0. File exists? $_fileExists');
// If the _file exists->read it: update initialized _json by what's in the _file
if (_fileExists) {
try {
//1. Read _jsonString<String> from the _file.
_myjsonString = await _filePath.readAsString();
print('1.(_readJson) _jsonString: $_myjsonString');
//2. Update initialized _json by converting _jsonString<String>->_json<Map>
_myjson = jsonDecode(_myjsonString);
print('2.(_readJson) _json: $_myjson \n - \n');
} catch (e) {
// Print exception errors
print('Tried reading _file error: $e');
// If encountering an error, return null
}
}
}
#override
void dispose() {
super.dispose();
}
// Delete Function-------------------------------------------
Future<int> deleteFile() async {
try {
final file = await _localFile;
await file.delete();
} catch (e) {
return 0;
}
return 0;
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("WebSocket Json"),
backgroundColor: Colors.redAccent),
body: Container(
alignment: Alignment.topCenter, //inner widget alignment to center
padding: const EdgeInsets.all(20),
child: Column(
children: [
Container(
//showing if websocket is connected or disconnected
child: connectedS1Status
? const Text("WEBSOCKET: CONNECTED")
: const Text("DISCONNECTED")),
Text("Cycles: $istcycles "),
],
)),
);
}
}

Is there a way to access data coming from BLE device from single file which keeps updating?

I want to access characteristic values of BLE from one dart file, What I am doing is that I am connecting the device from one activity and then sending the device info to all other activities. But to get values I have to write the same code again and again to all activities/dart files.
For example i am connecting device in an activity like this:
StreamBuilder<List<ScanResult>>(
stream: FlutterBlue.instance.scanResults,
initialData: [],
builder: (c, snapshot) => Column(
children: snapshot.data
.map(
(r) => ScanResultTile(
result: r,
onTap: () => Navigator.of(context)
.push(MaterialPageRoute(builder: (context) {
r.device.connect();
print('DEVICE CONNECTED');
return BluetoothConnectedSuccess(device: r.device);
Here device: r.device is the device that i have connected to my Flutter App. Now if i want to display device data i have to initilaze these lines of code everytime i jump to any screen/activity:
class BluetoothConnectedSuccess extends StatefulWidget {
const BluetoothConnectedSuccess({Key key, this.device}) : super(key: key);
final BluetoothDevice device;
#override
_BluetoothConnectedSuccessState createState() =>
_BluetoothConnectedSuccessState();
}
class _BluetoothConnectedSuccessState extends State<BluetoothConnectedSuccess> {
// BLE
final String SERVICE_UUID = "4fafc201-1fb5-459e-8fcc-c5c9c331914b";
final String CHARACTERISTIC_UUID = "beb5483e-36e1-4688-b7f5-ea07361b26a8";
bool isReady;
Stream<List<int>> stream;
List<int> lastValue;
List<double> traceDust = List();
#override
void initState() {
super.initState();
isReady = false;
connectToDevice();
}
connectToDevice() async {
await widget.device.connect();
discoverServices();
}
discoverServices() async {
List<BluetoothService> services = await widget.device.discoverServices();
services.forEach((service) {
if (service.uuid.toString() == SERVICE_UUID) {
service.characteristics.forEach((characteristic) {
if (characteristic.uuid.toString() == CHARACTERISTIC_UUID) {
characteristic.setNotifyValue(!characteristic.isNotifying);
stream = characteristic.value;
print(stream);
lastValue = characteristic.lastValue;
print(lastValue);
setState(() {
isReady = true;
});
}
});
}
});
}
_dataParser(List<int> data) {
var value = Uint8List.fromList(data);
print("stream.value: $value"); // stream.value: [33]
var hr = ByteData.sublistView(value, 0, 1);
print("Heart rate: ${hr.getUint8(0)}");
return hr.getUint8(0); // Heart rate: 33
}
It's creating a lot of mess to write the same code again and again to the activities where BLE data is needed.
Is there a way to only call this connected device from a single file instead of initializing the same code in every activity?
This is the link to my repo for a look at what I am doing on every activity/screen with BLE device data.
Please help me out as I am new to Flutter. Thank you
Firstly learn basic of state management using Get you can refer to my code here what happens is every time something changes or upadtes it will immediate show in UI using specific Get Widgets like Obx and GetX , these widgets listen to changes in value which are marked with obs (observable).
for exmaple :
Obx(
() => ble.isScan.value ? LinearProgressIndicator() : SizedBox(),
),
this will observe the changes in isScan value .
class BleServices extends GetxController {
FlutterBlue blue = FlutterBlue.instance;
BluetoothDevice d;
var connectedDevices = List<BluetoothDevice>().obs;
var scanResults = List<ScanResult>().obs;
var bleState = BluetoothState.off.obs;
var isScan = false.obs;
var scanRequire = false.obs;
var bluetoothServices = List<BluetoothService>().obs;
var discoveringServices = false.obs;
var characteristics = List<BluetoothCharacteristic>().obs;
#override
void onInit() async {
super.onInit();
final perb = await Permission.bluetooth.status.isGranted;
final perL = await Permission.location.status.isGranted;
if (perb && perL) {
getConnectedDevices();
} else {
await Permission.bluetooth.request();
await Permission.location.request();
}
isScanning();
state();
}
isDiscovering() async {}
getConnectedDevices() async {
final connectedDevice = await blue.connectedDevices;
connectedDevices.value = connectedDevice;
AppLogger.print('connected devices : $connectedDevice');
if (connectedDevice.length == 0) {
scanRequire.value = true;
searchDevices();
}
return connectedDevice;
}
searchDevices() {
// AppLogger.print('pppppppppppppp');
blue
.scan(timeout: Duration(seconds: 20))
.distinct()
.asBroadcastStream()
.listen((event) {
AppLogger.print(event.toString());
scanResults.addIf(!scanResults.contains(event), event);
});
Future.delayed(Duration(seconds: 20), () {
blue.stopScan();
Get.showSnackbar(GetBar(
message: 'scan is finished',
));
});
}
isScanning() {
blue.isScanning.listen((event) {
AppLogger.print(event.toString());
isScan.value = event;
});
}
state() {
blue.state.listen((event) {
AppLogger.print(event.toString());
bleState.value = event;
});
}
}

flutter shared preference null is returned

everyone
I saved List in shared preference.
Since List cannot be saved in shared preference, it is saved in json type.
I referenced the post on the stackover.
However, when reading the value stored in the preference, null was returned.
Please help.
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:real_lotto/lotto_number.dart';
class DisplayNumber extends StatefulWidget {
#override
_DisplayNumberState createState() => _DisplayNumberState();
}
class _DisplayNumberState extends State<DisplayNumber> {
String _number = '';
//initState called when the widget is mounted
void initState() {
super.initState();
if (_number == '') {
loadLotto().then((String s) => setState((() {
_number = s;
print(s);
})));
}
}
#override
Widget build(BuildContext context) {
print(_number);
return Scaffold();
}
}
Future<void> _saveLotto(number) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.setString('key', json.encode(number));
}
Future<String> loadLotto() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
var number = json.decode(prefs.getString('key'));
return (number);
}
List<dynamic> myNumber() {
var number = [];
number.add((List.generate(45, (index) => ++index)..shuffle()).sublist(0, 6));
number.add((List.generate(45, (index) => ++index)..shuffle()).sublist(0, 6));
number.add((List.generate(45, (index) => ++index)..shuffle()).sublist(0, 6));
number.add((List.generate(45, (index) => ++index)..shuffle()).sublist(0, 6));
number.add((List.generate(45, (index) => ++index)..shuffle()).sublist(0, 6));
for (var i = 0; i < number.length; i++) {
number[i].sort();
}
_saveLotto(number);
return number;
}

How to save event with sharedpreference in flutter

Hello I try to use this timeline package.
https://github.com/softmarshmallow/flutter-timeline
It's work fine to create timeline after press button but I don't success to save events with sharedpreference. I would like to restore history of the timeline at the initState.
TimelineEventDisplay get plainEventDisplay {
return TimelineEventDisplay(
child: TimelineEventCard(
title: Text("just now"),
content: Text("someone commented on your timeline ${DateTime.now()}"),
),
indicator: TimelineDots.of(context).circleIcon);
}
List<TimelineEventDisplay> events;
Widget _buildTimeline() {
return TimelineTheme(
data: TimelineThemeData(lineColor: Colors.blueAccent),
child: Timeline(
indicatorSize: 56,
events: events,
));
}
void _addEvent() {
setState(() {
events.add(plainEventDisplay);
});
}
#override
void initState() {
events = [
plainEventDisplay,
];
}
Create a SharedPref class so that it would be easy for you to manage things.
import 'package:shared_preferences/shared_preferences.dart';
import 'dart:convert';
class SharedPref {
read(String key) async {
final prefs = await SharedPreferences.getInstance();
if(prefs.getString(key) == null){
return null;
}
final map = jsonDecode(prefs.getString(key));
return map;
}
save(String key, value) async {
final prefs = await SharedPreferences.getInstance();
prefs.setString(key, jsonEncode(value));
}
remove(String key) async {
final prefs = await SharedPreferences.getInstance();
prefs.remove(key);
}
}
In your Flutter widget, create initState as follows:
SharedPref _prefs = SharedPref();
final events;
#override
void initState() async {
super.initState();
events = await _prefs.read('events');
}
void _addEvent() async {
setState(() {
events.add(plainEventDisplay);
});
await _prefs.save('events', events);
}

Correct way to call an api by provider in fflutter?

I have been trying to make a app in flutter where an api is called and data is updated in TextField
Used provider for state management, here is the code for it.
class ProfileProvider with ChangeNotifier {
var profileData;
String _url = "http://10.0.2.2:3000/api/v1/user/loggedin_user";
void getData() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
var token = prefs.getString('token');
var data = await http.get(
_url,
headers: {
"accept": "application/json",
"content-type": "application/json",
'Token': token,
},
);
var infoOfPerson = json.decode(data.body);
profileData = new ProfileObject(
name: infoOfPerson['name'],
mobile: infoOfPerson['mobile'],
email: infoOfPerson['email'],
role: infoOfPerson['role'],
);
notifyListeners();
}
ProfileObject get profileInfo {
return profileData;
}
}
I am getting the data fine, now i have to show it in the UI, but sometime data is populated, sometime its not. Can someone please point me the right direction why this is happening.
Here is the code for UI.
class Profile extends StatefulWidget {
#override
_ProfileState createState() => _ProfileState();
}
class _ProfileState extends State<Profile> {
final emailController = TextEditingController(text: '');
final nameController = TextEditingController(text: '');
final mobileController = TextEditingController(text: '');
var _isInit = true;
#override
void didChangeDependencies() {
if (_isInit) {
final profileData = Provider.of<ProfileProvider>(context);
profileData.getData();
if (profileData.profileInfo != null) {
emailController.text = profileData.profileInfo.name;
nameController.text = profileData.profileInfo.email;
mobileController.text = profileData.profileInfo.mobile;
}
_isInit = false;
super.didChangeDependencies();
}
}
#override
Widget build(BuildContext context) {
final profileData = Provider.of<ProfileProvider>(context);
return Scaffold(
drawer: NavigationDrawer(),
body: profileData.profileInfo == null
? Center(
child: CircularProgressIndicator(),
)
: Builder(
builder: (context) => SingleChildScrollView(
child: Padding(.....
Below the padding, there is normal TextField, can someone tell me why the data is being populated sometime and sometime its coming empty, even I wrapped it with CircularProgressIndicator() and a check the notifyListeners(); is not working there. The loader is not being shown and data is not being loaded.
Thanks
for StatelessWidget.
Inside the build method use:
Future.microtask(() async {
context.read<SomeProvider>().fetchSomething();
});
For StatefulWidgets if you want to call it once. Do this inside the initState() or didChangeDependencies (better if the latter). This will be called at the end of the frame which means after the build or rendering finishes..
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
context.read<SomeProvider>().fetchSomething();
});
}
EDIT: WidgetsBinding will also work on build. I forgot on why I used microtask lol
i've created a function which called nextTick, i call it in initState and it works for now, but want to see others method
void nextTick(Function callback, [int milliseconds = 0]) {
Future.delayed(Duration(milliseconds: 0)).then((_) {
callback();
});
}
then use it like below
#override
void initState() {
super.initState();
nextTick((){
ProfileProvider profileProvider = Provider.of<ProfileProvider>(context);
profileProvider.getProfile();
});
}
Edit: i store couple of variables to manage them on ui, like isLoading, hasError and errorMessage. Here is my provider class
class ProfileProvider extends ChangeNotifier {
bool _hasError = false;
bool _isLoading = true;
String _errorMsg = '';
Profile _profileResponse;
bool get hasError => _hasError;
bool get isLoading => _isLoading;
String get errorMsg => _errorMsg;
Profile get profileResponse => _profileResponse;
Future<void> getProfile() async {
this.setLoading = true;
this.setError = false;
this.setErrorMsg = '';
try {
await dio.post('$api/p/view', data: {}).then((res) {
print(res.data);
_profileResponse = Profile.fromJson(jsonDecode(res.data));
print(_profileResponse.data);
notifyListeners();
}).whenComplete(() {
this.setLoading = false;
});
} catch (e) {
this.setError = true;
this.setErrorMsg = '$e';
}
this.setLoading = false;
}
set setError(bool val) {
if (val != _hasError) {
_hasError = val;
notifyListeners();
}
}
set setErrorMsg(String val) {
if (val != null && val != '') {
_errorMsg = val;
notifyListeners();
}
}
set setLoading(bool val) {
_isLoading = val;
notifyListeners();
}
}