Dart/Flutter - HttpServer how to update image with websocket? - flutter

I am streaming my display to webserver. My current code is very basic and I don't know exactly how websockets work.
Right now the user have to refresh the page to get a new image. I would like to refresh on new image arrival.
Do I need to create a JavaScript page or can I render everything server side?
Here is my current code
import 'dart:io';
import 'package:flutter/services.dart';
const EventChannel eventChannel = EventChannel('bla.bla.io/screenshotStream');
dynamic image;
void handleNewImage(dynamic imageData) {
image = imageData;
}
registerScreenshotStreamSubscription() {
eventChannel.receiveBroadcastStream().listen(handleNewImage);
}
Future webServer() async {
registerScreenshotStreamSubscription();
HttpServer.bind(InternetAddress.anyIPv6, 3000).then((server) {
server.listen((HttpRequest request) async {
request.response.headers.set('Content-Type', 'image/png');
request.response.add(image);
request.response.close();
});
});
}

Related

Save image into gallery in Flutter

I try to save an image to gallery from a chat. I utilise for that gallery_saver package that does not helpfull. Please advise a proven package to save image into gallery. Or suggest how to fix the issue with method implementation.
void _saveNetworkImage(String url) async {
try {
GallerySaver.saveImage(url).then<void>((bool? success) {
if (success ?? false) {
GetIt.I
.get<NotificationService>()
.showSnackBar(context, S.current.image_saved_to_gallery);
} else {
GetIt.I
.get<NotificationService>()
.showSnackBar(context, S.current.error_image_saving_to_gallery);
}
}).onError((error, stackTrace) {
GetIt.I
.get<NotificationService>()
.showSnackBar(context, S.current.error_image_saving_to_gallery);
});
} catch (e) {
GetIt.I
.get<NotificationService>()
.showSnackBar(context, S.current.error_image_saving_to_gallery);
rethrow;
}
}
You can use path_provider package(link) for getting the local storage data and dio(link) or http(link) to get and save the image to the local storage.
Here's an example:
import 'dart:io';
import 'package:http/http.dart' as http;
import 'package:path/path.dart';
import 'package:path_provider/path_provider.dart';
var response = await http.get(imgUrl);
Directory tempDirectory = await getApplicationDocumentsDirectory();
File file = File(join(tempDirectory.path, 'image.png'));
file.writeAsBytesSync(response.bodyBytes);
Also, there's another package called image_gallery_saver(link) that makes saving images an easy task. You can checkout there package because we have to initially setup for some files. After that you can use this method to save image to the gallery.
_save() async {
var response = await Dio().get(
"https://ss0.baidu.com/94o3dSag_xI4khGko9WTAnF6hhy/image/h%3D300/sign=a62e824376d98d1069d40a31113eb807/838ba61ea8d3fd1fc9c7b6853a4e251f94ca5f46.jpg",
options: Options(responseType: ResponseType.bytes));
final result = await ImageGallerySaver.saveImage(
Uint8List.fromList(response.data),
quality: 60,
name: "hello",
);
print(result);
}

Flutter how to save data locally while phone screen is off

I wanna save data locally while app running in background and phone screen is off , how can u do that using hive DB.
I just try to save data locally while app is running everything is fine now I want to do the same thing while screen phone is off
I don't think Hive supports opening boxes in multiple isolates, so you will have to close the box in the main isolate, update it in your background isolate and reopen it in the main isolate.
This might help
Here is an example code of communication between two isolates:
import 'dart:io';
import 'dart:async';
import 'dart:isolate';
Future<SendPort> initIsolate() async {
Completer completer = new Completer<SendPort>();
ReceivePort isolateToMainStream = ReceivePort();
isolateToMainStream.listen((data) {
if (data is SendPort) {
SendPort mainToIsolateStream = data;
completer.complete(mainToIsolateStream);
} else {
print('[isolateToMainStream] $data');
}
});
Isolate myIsolateInstance = await Isolate.spawn(myIsolate,isolateToMainStream.sendPort);
return completer.future;
}
void myIsolate(SendPort isolateToMainStream) {
ReceivePort mainToIsolateStream = ReceivePort();
isolateToMainStream.send(mainToIsolateStream.sendPort);
mainToIsolateStream.listen((data) {
print('[mainToIsolateStream] $data');
exit(0);
});
isolateToMainStream.send('This is from myIsolate()');
}
void main() async {
SendPort mainToIsolateStream = await initIsolate();
mainToIsolateStream.send('This is from main()');
}

how to add http caching using "dio_http_cache" package to flutter

I have a code that I use to download data, through a query (get) and display it. But I want to change something, I want to cache the data and display it. So that if the device was offline, I could display the latest data. Someone is already doing this, I will be grateful for your help)
import 'package:dio/dio.dart';
import 'package:dio_http_cache/dio_http_cache.dart';
import 'package:test_axles/models/seals_model.dart';
import 'package:test_axles/setting_var/globalvar.dart' as global;
class ApiProvider {
final Dio _dio = Dio();
final String _url = global.urlVar;
Future<Objects> fetchSealsList() async {
try {
Response response = await _dio.get(_url);
return Objects.fromJson(response.data);
} catch (error, stacktrace) {
return Objects.withError(" ");
}
}
}

A more efficient way to transfer files over a socket in Dart?

I am trying to build a flutter app to send files to any device over my local network. I'm using sockets to send and recieve the data. The transfer is successful but the whole process is very resource(RAM) intensive(I believe because of the Uint8List and builder that is created on the server and client side.)
Because of it I can only send files of size 100-200 MB's without running out of memory.
I was wondering whether it was possible to send data more efficiently by optimizing the code or any other method of file transfer altogether.
Server side code:
import 'dart:io';
import 'dart:typed_data';
void main() async {
final server = await ServerSocket.bind('localhost', 2714);
server.listen((client) {
handleClient(client);
});
}
void handleClient(Socket client) async {
File file = File('1.zip');
Uint8List bytes = await file.readAsBytesSync();
print("Connection from:"
"${client.remoteAddress.address}:${client.remotePort}");
client.listen((Uint8List data) async {
await Future.delayed(Duration(seconds: 1));
final request = String.fromCharCodes(data);
if (request == 'Send Data') {
client.add(bytes);
}
client.close();
});
}
Client side code:
import 'dart:io';
import 'dart:typed_data';
int size = 0;
void main() async {
final socket = await Socket.connect('localhost', 2714);
print("Connected to:" '${socket.remoteAddress.address}:${socket.remotePort}');
socket.write('Send Data');
await socket.listen((Uint8List data) async {
await Future.delayed(Duration(seconds: 1));
dataHandler(data);
size = size + data.lengthInBytes;
print(size);
print("ok: data written");
});
await Future.delayed(Duration(seconds: 20));
socket.close();
socket.destroy();
}
BytesBuilder builder = new BytesBuilder(copy: false);
void dataHandler(Uint8List data) {
builder.add(data);
if (builder.length <= 1049497288) {
// file size
Uint8List dt = builder.toBytes();
writeToFile(
dt.buffer.asUint8List(0, dt.buffer.lengthInBytes), '1(recieved).zip');
}
}
Future<void> writeToFile(Uint8List data, String path) {
// final buffer = data.buffer;
return new File(path).writeAsBytes(data);
}
I'm still very much new to Dart and Flutter, any help would be appreciated, thanks.
#pskink gave you most of the answer, but you are right that you're running into issues because you are reading the entire file into memory on the server side, and then accumulating all the received bytes into memory before writing on the client side.
You can stream the data from disk to socket on the server side, and stream from socket to disk on the client side. Here's the full code:
import 'dart:io';
import 'dart:typed_data';
void main() async {
final server = await ServerSocket.bind('localhost', 2714);
server.listen((client) async {
await File('1.zip').openRead().pipe(client);
});
}
import 'dart:io';
import 'dart:typed_data';
void main() async {
var socket = await Socket.connect('localhost', 2714);
try {
print(
"Connected to:" '${socket.remoteAddress.address}:${socket.remotePort}');
socket.write('Send Data');
var file = File('1_received.zip').openWrite();
try {
await socket.map(toIntList).pipe(file);
} finally {
file.close();
}
} finally {
socket.destroy();
}
}
List<int> toIntList(Uint8List source) {
return List.from(source);
}
File.openRead, File.openWrite and the socket are all asynchronous streams. See https://dart.dev/tutorials/language/streams for more details.
The pipe calls are hiding all of the work here, but in other cases you might use the streams with the async for (var element in stream) statement.
There's one thing to keep in mind here, in await File('1.zip').openRead().pipe(client), there is no corresponding close method for openRead. The file handle is automatically closed once you reach the end of the stream. I've had some issues when reading many files in quick succession, even if each was read until the end before the next one was opened, resulting in "Too many open files" errors.
I didn't investigate further because I was reading small files and switched to using readAsBytesSync, but it's possible the handles are only closed during GC or there's some other reason why they're not immediately released, which puts a limit to the number of files you can read this way within a short time span.

how to save data in Hive database when receiving data in the background?

I have an issue saving data to Hive when receiving Firebase Cloud Messaging (FCM) push notification data when the app is in the background.
I have a static method to set up hive like this
static Future<void> setUpHive() async {
try {
await Hive.initFlutter();
if (!Hive.isBoxOpen("Box Name")) {
await Hive.openBox("Box Name");
}
} catch (error) {
print(error.toString());
}
}
I use that setUpHive static method in main function like this
Future<void> main() async {
await HiveHelper.setUpHive();
runApp(
MyApp(),
);
}
when the app is in the background, and then it receives FCM message, then this code below will be called. after that I try change the data stored in the Hive box
Future<void> firebaseMessagingBackgroundHandler(RemoteMessage message) async {
// when receive FCM message when app is in the background, this block will be executed
// set up the hive first
await HiveHelper.setUpHive();
// then I try to change the data stored in the Hive box
final myBox = Hive.box("BOX NAME");
myBox.put("key", 12345);
}
it seems okay after receiving FCM background data, but when I fully close the app, and the main called again I have error when trying to open the box like this
static Future<void> setUpHive() async {
try {
await Hive.initFlutter();
if (!Hive.isBoxOpen("Box Name")) {
await Hive.openBox("Box Name"); // Error in this line
}
} catch (error) {
print(error.toString());
}
}
the error is:
HiveError: This should not happen. Please open an issue on GitHub.
E/flutter (13142): [ERROR:flutter/lib/ui/ui_dart_state.cc(199)]
Unhandled Exception: HiveError: This should not happen. Please open an
issue on GitHub. E/flutter (13142): #0 BinaryReaderImpl.readFrame
(package:hive/src/binary/binary_reader_impl.dart:250:7)
E/flutter
I try to find the solution, and I find similar issue from here about Using Hive DB in a Background Process and it is said
leisim:
Unfortunately, Hive does not support opening boxes in multiple
isolates. That means you can either close the box in the main isolate,
update it in your background isolate and reopen it in the main isolate
or you pass the data from the background to the main isolate and
perform the update there...
I am new in Flutter, and I don't understand what he said. please help :(
You can try the following code. The basic idea is to send data from background isolate to main isolate.
Future<void> backgroundMessageHandler(RemoteMessage msg){
IsolateNameServer.lookupPortByName('main_port')?.send(msg);
}
#override
void initState(){
super.initState();
ReceivePort receivePort = ReceivePort();
IsolateNameServer.registerPortWithName(receivePort.sendPort,'main_port');
receivePort.listen((message) {
if(message is RemoteMessage){
//TODO: save your data in hive box
}
}
}
You need to close your hive box in the main isolate once app goes into background. When it does, you need to CRUD in the background isolate. If you want to sync data between two isolates (because they don't share the same hive data) then you need a two way communication between isolates.
Here is an example code of communicating between two isolates.
import 'dart:io'; // for exit();
import 'dart:async';
import 'dart:isolate';
Future<SendPort> initIsolate() async {
Completer completer = new Completer<SendPort>();
ReceivePort isolateToMainStream = ReceivePort();
isolateToMainStream.listen((data) {
if (data is SendPort) {
SendPort mainToIsolateStream = data;
completer.complete(mainToIsolateStream);
} else {
print('[isolateToMainStream] $data');
}
});
Isolate myIsolateInstance = await Isolate.spawn(myIsolate, isolateToMainStream.sendPort);
return completer.future;
}
void myIsolate(SendPort isolateToMainStream) {
ReceivePort mainToIsolateStream = ReceivePort();
isolateToMainStream.send(mainToIsolateStream.sendPort);
mainToIsolateStream.listen((data) {
print('[mainToIsolateStream] $data');
exit(0);
});
isolateToMainStream.send('This is from myIsolate()');
}
void main() async {
SendPort mainToIsolateStream = await initIsolate();
mainToIsolateStream.send('This is from main()');
}
for more go to https://medium.com/#lelandzach/dart-isolate-2-way-communication-89e75d973f34