Best way to keep fetching new data from MySQL - flutter

I need some suggestions or help please. I am trying to do a small real time chat application, most tutorials that I have been looking basically use firebase, but I have been using MySQL already for other parts of my small projects, I am not sure if I understood Stream correctly, but as far as I know I think that the data does keep fetching, I tried it but it didn't update the new data, and besides StreamBuilder being a little hard to use, I went for the easiest method using a regular http and displaying it by using a function which will be run at the initiation of page like this:
void initState() {
super.initState();
_getChat();
}
_getChat() {
Pulling.getChatsContents().then((value) {
setState(() {
_chatContent = value;
_chatLength = "${value.length}";
});
});
}
Storing them here:
List<Chats> _chatContent;
String _chatLength = '0';
where the List<Chats> is the following:
class Chats {
final String senderid;
final String msgcontent;
Chats(
{this.senderid,
this.msgcontent});
factory Chats.fromJson(Map<String, dynamic> json) {
return Chats(
senderid: json['sender_id'] as String,
msgcontent: json['msg_content'] as String,
);
}
}
and the Pulling is:
class Pulling {
static const ROOT = 'https://example.com/chatsContent.php';
static StreamController streamChat = StreamController();
static Future<List<Chats>> getChatsContents() async {
try {
var myUser = '2';
var map = Map<String, dynamic>();
map['user'] = myUser;
final response = await http.post(ROOT, body: map);
if (200 == response.statusCode) {
final chatsParsed =
json.decode(response.body).cast<Map<String, dynamic>>();
List<Chats> listChat =
chatsParsed.map<Chats>((json) => Chats.fromJson(json)).toList();
return listChat;
} else {
return List<Chats>();
}
} catch (e) {
return List<Chats>();
}
}
}
So basically with all these I am getting the data and displaying, but when new data is added, I do not get the update, there may be a way to do it with Stream, but I am actually quite confused combining it with http, so I was thinking if it is possible and if it is efficient for the app if I added this to the void initState:
_timer = Timer.periodic(Duration(seconds: 1), (timer) => _getChat());
also calling _timer at the beginning:
Timer _timer;
Thank you guys for reading all this and helping me!
UPDATE
The way that I display the fetched data is as follows:
return Column(
children: [
for (var i = 0; i < int.parse(_chatLength); i++)
Container(
child: Text(_chatContent[i].msgcontent),
),
]);

Have you though about checking out about Redux ? It's a really good thing to use for realtime applications. It's quite long to understand, you can find really good tutorials online like this one which is a really good one.
Let me know if you need some help ;)

In my opinion you should use WebSocket instead of HTTP in order to fetch new data instantly.

Related

Why is this listener never called?

I'm trying to use Riverpod for my project, however I'm hitting some issues.
I am not sure that I'm using it very well so don't hesitate to tell me if you see anything wrong with it :)
First I have my authProvider:
final authRepoProvider = ChangeNotifierProvider.autoDispose((ref) {
return AuthRepository();
});
class AuthRepository extends ChangeNotifier {
String? token;
Future signIn(String username, String password) async {
// Do the API calls...
token = tokenReturnedByAPI;
notifyListeners();
}
}
Then I have a service, let's say it allows to fetch blog Articles, with a stream to get live update about those.
class ArticleService {
StreamController<Article> _streamCtrl;
String? _token;
API _api;
ArticleService(this._api) : _streamCtrl = StreamController<Article>() {
_api.onLiveUpdate((liveUpdate) {
_streamCtrl.add(liveUpdate);
});
}
Stream<Article> get liveUpdates => _streamCtrl.stream;
Future markArticleAsRead(String id) async {
await _api.markAsRead(_token, id);
}
}
For that article service I would like to keep the current token up to date, but I don't want to rebuild the entire service every time the token changes as there are listeners and streams being used.
For that I would prefer to listen to the changes and update it myself, like such:
final articleServiceProvider = Provider.autoDispose((ref) {
final service = ArticleService(
ref.read(apiProvider),
);
ref.listen<AuthRepository>(authRepositoryProvider, (previous, next) {
service._token = next.token;
}, fireImmediately: true);
return service;
});
That piece of code seems correct to me, however when I authenticate (authRepository.token is definitely set) and then try to invoke the markArticlesAsRead method I end up with an empty token.
The ref.listen is never called, even tho AuthRepository called notifyListeners().
I have a feeling that I'm using all that in a wrong way, but I can't really pinpoint what or where.
Try ref.watch
final articleServiceProvider = Provider.autoDispose((ref) {
final service = ArticleService(
ref.read(apiProvider),
);
final repo = ref.watch<AuthRepository>(authRepositoryProvider);
service._token = repo.token;
return service;
});

What is the best way to work with files in Flutter?

I'm a junior working with flutter and hit a problem.
I need to open a file, read and compare some data everytime the app opens and then change some of that data as the app progress. We tried using .txt files to read and write some text, but when we had to look for something in the file was too complicated to change it and the file is not accessibe only on the device running the app. We also thought of using xml files but I don't know if is a good idea.
What would be a pratical solution for this situation, as the file needs to be opened all the time. Thanks.
Let's say our JSON looks like this:
{
title: 'some text',
values: [1,5,2,4,1,3],
}
And we want to make a UI that allows us to add values and to edit the title. First let's write a method to write and read from the JSON file:
Future<void> _write(Map<String, dynamic> value) async {
File f = File(_fileLocation);
String jsonStr = jsonEncode(value);
await f.writeAsString(jsonStr);
}
Future<Map<String, dynamic>> _read() async {
File f = File(_fileLocation); // maybe move this into a getter
final jsonStr = await f.readAsString();
return jsonDecode(jsonStr) as Map<String, dynamic>;
}
This way, the rest of the app should be trivial, but let's add a method to update the title and a method to add a new number:
Future<void> _updateTitle(String title) async {
var values = await _read();
values['title'] = title;
await _write(values);
}
Future<void> _addNumber(int number) async {
var values = await _read();
values['values'].push(number);
await _write(values);
}
Types with JSON and Dart can be a bit weird, so it is possible you need to use the as keyword when reading from the list:
Future<void> _addNumber(int number) async {
var values = await _read();
var valueList = (values['values'] as List<int>);
valueList.push(number);
values['values'] = valueList;
await _write(values);
}
Hopefully, this example helps

When I am using the provider package in Flutter to load data from an API into a list it repeatedly calls the API, how do I fix it?

I am trying to lode data from an api call that retrieves a map, I am able to get the map from the api to display how I want it to, however it repeatedly calls the api meaning the list keeps on refreshing. Even though I have tried setting the listener to false, it works but I have to manually refresh the app for it to work?
Additional Info: Assigning and Retrieving Data
import 'package:http/http.dart' as http;
class Stores with ChangeNotifier {
var s_length;
Future<List<Store>> getStores(String storeCatName) async {
final queryParameters = {
"store_category_name": storeCatName,
};
try {
//TODO this is the issue - must fix.
final uri = Uri.http("url", 'url', queryParameters);
//print(uri);
final response = await http.get(uri);
//print(response.statusCode);
//print(response.body);
if (response.statusCode == 200) {
final List<Store> stores = storeFromJson(response.body);
_stores = stores;
//print(_stores);
print("lenght: ${_stores.length}");
Store store;
for(store in _stores) {
store.products = Products().products(store.storeId);
}
//check if this is correct
notifyListeners();
//return stores;
} else {
print("error1");
return List<Store>();
}
} catch (e) {
print(e.toString());
return List<Store>();
}
//notifyListeners();
print(_stores);
}
List<Store> get favoriteItems {
//return _stores.where((storeItem) => storeItem.isFavorite).toList();
}
bool isNotFull(){
if (_stores.isEmpty){
return true;
} else {
return false;
}
}
int get numberOfStores{
return s_length;
}
List<Store> _stores = [];
List<Store> stores (String storeCatName){
getStores(storeCatName);
//print("cpp; + $s_length");
//notifyListeners();
return _stores;
}
}
final storesProvider = Provider.of<Stores>(
context, listen: false
);
storesProvider.getStores(categoryName);
final providerStoreList = storesProvider.stores(category.storeCategoryName);
Additional Info: Builder for List:
child: ListView.builder(
itemCount: providerStoreList.length,
itemBuilder: (context, index) => ChangeNotifierProvider.value(
value: providerStoreList[index],
child: StoreItem(),
)));
If any additional information is required just let me know. Any help would be greatly appreciated.
Thanks
Use
listen: false;
var ourClient = Provider.of<CartBlock>(context, listen: false);
Setting the listener to false means that your widget won't build again when notifyListeners() is called.
So, that might not be the issue.
The only reason I can think of is calling the API again from the build method,
which might happen if you are using a ListView builder.
So, every time you might be scrolling the ListView your API would call again.

I want to execute a function when the Flutter app starts

I want to send an ID to the server and receive json when the app is launched.
Flow
1.Start my app (Show splash screen)
2.Json request to server
3.If there is data, display page1. If not, display page2
it seems you my need to get a bit more learning about Flutter, my sugest is to start with this one only 10 euros will give you base from where will be easier to learn the rest, that said, to get a databse i'm using this code:
//lib/services/networking_service.dart
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
class NetworkHelper {
final String json;
final url = 'HERE YOU CAN PUT YOUR API URL';
NetworkHelper(this.json);
Map<String, String> headers = {
"Content-type": "application/x-www-form-urlencoded"
};
Future getData(BuildContext context) async {
http.Response response = await http.post(url, body: json, headers: headers);
if (response.statusCode == 200) {
Map<String, dynamic> decodedResp = jsonDecode(response.body);
print(decodedResp);
return decodedResp;
} else {
print(response.statusCode);
return null;
}
}
}
You can call it from your main like this:
static getCategories(BuildContext context) async {
String json =
'q={"f":"listCategories","Store_id":"$storeId","LANG":"$lang","UID":"$uid"}';
//THIS json VARIABLE IS WHERE YOU NEED TO PUT YOUR API CALL LĂ“GIC TO GET THAT ID, I LEAVE THIS FOR YOUR BETTER UNDERSTANDING
NetworkHelper networkHelper = NetworkHelper(json);
var decodedResp = await networkHelper.getData(context);
final CategoriesModel respData = CategoriesModel.fromJson(decodedResp);
print(respData);
//HERE YOU MAY RETURN O STORE IN PROVIDER YOUR RESPONSE AND SEND THE USER TO THE PAGE YOU CONSIDER
}
If you need more help I'm happy to help, but consider taking the course o learn a bit more, it will be lots more easy and enjoyable after.
use SchedulerBinding it runs when page is opened and widgets are build.
#override
void initState() {
super.initState();
SchedulerBinding.instance.addPostFrameCallback((_) {
// your code after page opens,splash keeps open until work is done
});
}
#override
void initState() {
super.initState();
Timer(
Duration(seconds: 3),// you can do your stuff here when splash screen run
() => Navigator.of(context).pushReplacement(MaterialPageRoute(
builder: (BuildContext context) => LoginScreen())));}
and please put this code into the spalsh screen

What is the best way to make a network call?

I am fairly new to Flutter. I would like to know what is the best way in terms of coding best practices to do a network call.
I searched on internet (including Stackoverflow) on how to make the REST call (GET, POST) and found some code samples. For example, one of them is given below.
new RaisedButton(
onPressed: () async {
Post newPost = new Post(
userId: "123", id: 0, title: titleControler.text, body: bodyControler.text);
Post p = await createPost(CREATE_POST_URL,
body: newPost.toMap());
print(p.title);
},
)
Now, I don't think it's a good idea to club everything in onPressed(). I am especially interested to know how to fit a network call before a page load (or update after the data is fetched). I know it's done by setState(). But would like to know how the different pieces are put together to write a best code.
Any help will be highly appreciated.
The best way is to keep every piece short and specific to one task only. This is not specific to the Flutter (and applies to any coding in general). One type of doing it is as mentioned below :
Let's say you want to fetch a list of employees from the API.
Define Employee class, specify mandatory/non-mandatory attributes etc. And also write .fromJson() to instantiate.
Start with didChangeDependencies in your Stateful widget like below.
#override
void didChangeDependencies() {
super.didChangeDependencies();
_requestEmployeeListFromAPI();
}
The _requestDataFromAPI() function will just be a pointer for the network call as follows :
Future<void> _requestEmployeeListFromAPI() async {
try {
_myList = await network.getEmployeeList();
} catch (exception) {
print(exception);
// ...
} else {
// show exception
}
} finally {
if (mounted) {
setState(() => _isLoadingCompleted = true);
}
}
}
Handle your UI based on the value of _isLoadingCompleted
Now, your network file may consists all the network calls to be done in the project. Example below :
Future<List<Employee>> getEmployeeList() async {
try {
http.Response response = await http.post(
"$getEmployeeListUrl",
);
Map<String, dynamic> decodedBody = json.decode(response.body);
print(decodedBody);
List<Employee> employeeList = (decodedBody["EmployeeList"] as List)
.map((element) => Employee.fromJson(element))
.toList();
return employeeList;
} catch (e) {
print(e);
}
}
This way, every piece is written at a different position and is very easy to enhance / debug / find bug, if any.