Drawing over an image downloaded from remote server - dart

I need to do the following:
Download a PNG resource from a server
Draw several rectangles over that image with different color depending on state
Display that image in Zoomable Image View
I have a working code in an Android app using Canvas, but I cannot figure out how to do that with Flutter.
Here is the code that downloads the resource:
static Future<File> getImageFromUrl(String url) async {
final directory = await getApplicationDocumentsDirectory();
final file = File("$directory/${_getSHA(url)}.png");
if (await file.exists()) {
// Returns the cached file
} else {
final response = await http.get(url);
if (response.statusCode >= 200 && response.statusCode < 300) {
await file.writeAsBytes(response.bodyBytes);
} else {
return null;
}
}
return file;
}
What should I do next? I tried using PictureRecorder and Canvas, but I cannot find a way to draw image from the file on those canvas and then convert it to Image because I cannot extract width and height from the file.
EDIT:
Below is the Android code equivalent that I would like to implement in Flutter.
// Here we have a bitmap from a file
Bitmap mapBitmap = getBitmap();
Canvas mapCanvas = new Canvas(mapBitmap);
mapDrawable.setBounds(0, 0, mapCanvas.getWidth(), mapCanvas.getHeight());
mapDrawable.draw(mapCanvas);
canvasWidth = mapCanvas.getWidth();
canvasHeight = mapCanvas.getHeight();
Paint paint = new Paint();
for (java.util.Map.Entry<String, MapObject> entry : this.mapObjects.entrySet()) {
MapObject mapObject = entry.getValue();
paint.setColor(getContext().getResources().getColor(mapObject.getBackgroundColor()));
paint.setAlpha(100);
mapCanvas.drawRect((int) (mapObject.getPosX() * scaleX),
(int) (mapObject.getPosY() * scaleY),
(int) ((mapObject.getPosX() + mapObject.getWidth()) * scaleX),
(int) ((mapObject.getPosY() + mapObject.getHeight()) * scaleY),
paint);
}
photoView.setImageBitmap(mapBitmap);

I finally managed to solve the issue!
I created a renderer that creates a composite image (background from the remote resource and adds rectangles in the foreground).
The renderer:
class MapRenderer {
ui.Image _mapBackgroundImage;
Future<ui.Codec> renderMap(String url, List<Sensor> sensors) async {
await _loadMapBackground(url);
var renderedMapImage = await _updateSensors(sensors);
var byteD = await renderedMapImage.toByteData(
format: ui.ImageByteFormat.png);
return ui.instantiateImageCodec(Uint8List.view(byteD.buffer));
}
Future<ui.Image> _updateSensors(List<Sensor> sensors) async {
ui.PictureRecorder recorder = ui.PictureRecorder();
Canvas c = Canvas(recorder);
var paint = ui.Paint();
c.drawImage(_mapBackgroundImage, ui.Offset(0.0, 0.0), paint);
for (Sensor s in sensors) {
paint.color = (s.availability ? CustomColors.npSensorFree : CustomColors
.npSensorOccupied);
c.drawRect(
ui.Rect.fromPoints(ui.Offset(s.posX, s.posY),
ui.Offset(s.posX + s.width, s.posY + s.height)),
paint,
);
}
return recorder
.endRecording()
.toImage(_mapBackgroundImage.width, _mapBackgroundImage.height);
}
Future<void> _loadMapBackground(String url) async {
var imageBytes = await _getLocalCopyOrLoadFromUrl(url);
if (imageBytes != null) {
_mapBackgroundImage = await _getImageFromBytes(imageBytes);
} else {
return null;
}
}
Future<ui.Image> _getImageFromBytes(Uint8List bytes) async {
var imageCodec = await ui.instantiateImageCodec(bytes);
var frame = await imageCodec.getNextFrame();
return frame.image;
}
Future<Uint8List> _getLocalCopyOrLoadFromUrl(String url) async {
final directory = await getApplicationDocumentsDirectory();
final file = File("${directory.path}/${_getSHA(url)}.png");
if (await file.exists()) {
return await file.readAsBytes();
} else {
Uint8List resourceBytes = await _loadFromUrl(url);
if (resourceBytes != null) {
await file.writeAsBytes(resourceBytes);
return resourceBytes;
} else {
return null;
}
}
}
Future<Uint8List> _loadFromUrl(String url) async {
final response = await http.get(url);
if (response.statusCode >= 200 && response.statusCode < 300) {
return response.bodyBytes;
} else {
return null;
}
}
String _getSHA(String sth) {
var bytes = utf8.encode(sth);
var digest = sha1.convert(bytes);
return digest.toString();
}
void dispose() {
_mapBackgroundImage.dispose();
}
}
And to supply the image to the ZoomableImage I created a custom ImageProvider:
class MapImageProvider extends ImageProvider<MapImageProvider> {
final String url;
final List<Sensor> sensors;
final MapRenderer mapRenderer = MapRenderer();
MapImageProvider(this.url, this.sensors);
#override
ImageStreamCompleter load(MapImageProvider key) {
return MultiFrameImageStreamCompleter(
codec: _loadAsync(key),
scale: 1.0,
informationCollector: (StringBuffer information) {
information.writeln('Image provider: $this');
information.write('Image key: $key');
});
}
Future<ui.Codec> _loadAsync(MapImageProvider key) async {
assert(key == this);
return await mapRenderer.renderMap(url, sensors);
}
#override
bool operator ==(Object other) =>
identical(this, other) ||
other is MapImageProvider &&
runtimeType == other.runtimeType &&
url == other.url;
#override
int get hashCode => url.hashCode;
#override
String toString() => '$runtimeType("$url")';
#override
Future<MapImageProvider> obtainKey(ImageConfiguration configuration) {
return SynchronousFuture<MapImageProvider>(this);
}
}
If anybody knows a better way to convert an Image to Codec or to even skip this step, please comment (MapRenderer.renderMap function).

In general to simply display an image from the internet you can use the Image.network constructor. If you want to further customize the interaction, for example showing rectangles based on its loading state, you can use the Image class and pass a NetworkImage to its constructor. The NetworkImage allows you to listen to loading and error events.
To draw above the Image I would simply suggest using the Stack Widget.
If you wanna add zooming functionality to the image, you should consider using the zoomable_image or photo_view package to replace the Image in the code below.
Also, if caching is necessary you can use the CachedNetworkImageProvider from the cached_network_image package.
The example below shows a yellow rectangle on a loading image, a green rectangle on a fully loaded image and a red rectangle if the loading crashed. It is a full application, you can copy & paste it in your IDE and try it out.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Network Image Download',
theme: ThemeData(),
home: MainPage(),
);
}
}
class MainPage extends StatefulWidget {
#override
State<StatefulWidget> createState() => MainPageState();
}
class MainPageState extends State<MainPage> {
ImageProvider provider;
bool loaded;
bool error;
#override
void initState() {
super.initState();
loaded = false;
error = false;
provider = NetworkImage('https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png');
provider.resolve(ImageConfiguration()).addListener((_, __) {
setState(() {
loaded = true;
});
}, onError: (_, __) {
setState(() {
error = true;
});
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Stack(
alignment: Alignment.center,
children: <Widget>[
Image(image: provider),
Container(
width: 75.0,
height: 75.0,
color: colorByState(),
)
],
),
),
);
}
Color colorByState() {
if (error) {
return Colors.red;
} else if (loaded) {
return Colors.green;
} else {
return Colors.yellow;
}
}
}

Related

Lost connection to device. Exited (sigterm) - Flutter Crash

My Flutter app is crashing on loading
It operates a FutureBuilder and I believe this to be where the issue comes from.
My app makes an API Call and returns the data to a map marker.
When i have the FutureBuilder return a list view it works fine.
However, when i change it to return a Stack containing my Map SDK and the buttons to call the API it crashes on start up.
Relevant code is below, thank you!
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
Future<Stations> stations;
BuildContext _context;
MapMarkerExample _mapMarkerExample;
#override
void initState() {
stations = API_Call().fetchStations();
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Example 1'),
),
body: Container(
child: FutureBuilder<Stations>(
future: stations,
builder: (context, snapshot) {
if (snapshot.hasError) {
return Text("Error");
}
if (snapshot.connectionState == ConnectionState.done) {
return
Stack(
children: [
HereMap(onMapCreated: _onMapCreated),
Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
button('Stations Near Me', _anchoredMapMarkersButtonClicked),
button('Clear', _clearButtonClicked),
],
),
],
),
],
);
}
return Text("Loading");
}
)
)
);
}
api_call.dart
class API_Call {
Future<Stations> fetchStations() async {
var client = http.Client();
final response = await client.get(
'https://transit.hereapi.com/v8/stations?in=x,-x&return=transport&apiKey=API_KEY');
if (response.statusCode == 200) {
return Stations.fromJson(jsonDecode(response.body));
} else {
throw Exception('Failed to load stations');
}
}
}
api_manager.dart
typedef ShowDialogFunction = void Function(String title, String message);
class MapMarkerExample{
void showAnchoredMapMarkers() {
print('step5');
GeoCoordinates geoCoordinates = _callGeoCoordinates();
// use the coords .. to add a marker
_addCircleMapMarker(geoCoordinates, 0);
_addPOIMapMarker(geoCoordinates, 1);
print('step6');
}
GeoCoordinates _callGeoCoordinates() {
print('step7');
var stations;
Future<Stations> fetchStations() async {
stations = await API_Call().fetchStations();
for (Station stations in stations) {
GeoCoordinates geoCoordinates = GeoCoordinates (stations.place.location.lat, stations.place.location.lng);
// use the coords .. to add a marker
_addCircleMapMarker(geoCoordinates, 0);
_addPOIMapMarker(geoCoordinates, 1);
}
}
}
HereMapController _hereMapController;
List<MapMarker> _mapMarkerList = [];
MapImage _poiMapImage;
MapImage _circleMapImage;
ShowDialogFunction _showDialog;
List<MapMarker3D> _mapMarker3DList = [];
MapMarkerExample(ShowDialogFunction showDialogCallback, HereMapController hereMapController) {
_showDialog = showDialogCallback;
_hereMapController = hereMapController;
double distanceToEarthInMeters = 8000;
_hereMapController.camera.lookAtPointWithDistance(
GeoCoordinates(x, -x), distanceToEarthInMeters);
// Setting a tap handler to pick markers from map.
_setTapGestureHandler();
_showDialog("Note", "Tap markers for more.");
}
void clearMap() {
for (var mapMarker in _mapMarkerList) {
_hereMapController.mapScene.removeMapMarker(mapMarker);
}
_mapMarkerList.clear();
for (var mapMarker3D in _mapMarker3DList) {
_hereMapController.mapScene.removeMapMarker3d(mapMarker3D);
}
_mapMarker3DList.clear();
}
Future<void> _addPOIMapMarker(GeoCoordinates geoCoordinates, int drawOrder) async {
// Reuse existing MapImage for new map markers.
if (_poiMapImage == null) {
Uint8List imagePixelData = await _loadFileAsUint8List('assets/poi.png');
_poiMapImage = MapImage.withPixelDataAndImageFormat(imagePixelData, ImageFormat.png);
}
Anchor2D anchor2D = Anchor2D.withHorizontalAndVertical(0.5, 1);
MapMarker mapMarker = MapMarker.withAnchor(geoCoordinates, _poiMapImage, anchor2D);
mapMarker.drawOrder = drawOrder;
Metadata metadata = new Metadata();
metadata.setString("key_poi", "Next Departures");
mapMarker.metadata = metadata;
_hereMapController.mapScene.addMapMarker(mapMarker);
_mapMarkerList.add(mapMarker);
}
Future<void> _addCircleMapMarker(GeoCoordinates geoCoordinates, int drawOrder) async {
// Reuse existing MapImage for new map markers.
if (_circleMapImage == null) {
Uint8List imagePixelData = await _loadFileAsUint8List('assets/circle.png');
_circleMapImage = MapImage.withPixelDataAndImageFormat(imagePixelData, ImageFormat.png);
}
MapMarker mapMarker = MapMarker(geoCoordinates, _circleMapImage);
mapMarker.drawOrder = drawOrder;
_hereMapController.mapScene.addMapMarker(mapMarker);
_mapMarkerList.add(mapMarker);
}
Future<Uint8List> _loadFileAsUint8List(String assetPathToFile) async {
// The path refers to the assets directory as specified in pubspec.yaml.
ByteData fileData = await rootBundle.load(assetPathToFile);
return Uint8List.view(fileData.buffer);
}
void _setTapGestureHandler() {
_hereMapController.gestures.tapListener = TapListener.fromLambdas(lambda_onTap: (Point2D touchPoint) {
_pickMapMarker(touchPoint);
});
}
void _pickMapMarker(Point2D touchPoint) {
double radiusInPixel = 2;
_hereMapController.pickMapItems(touchPoint, radiusInPixel, (pickMapItemsResult) {
// Note that 3D map markers can't be picked yet. Only marker, polgon and polyline map items are pickable.
List<MapMarker> mapMarkerList = pickMapItemsResult.markers;
if (mapMarkerList.length == 0) {
print("No map markers found.");
return;
}
});
}
}
In api_manager.dart, this looks very suspicous, and you aren't returning anything from this function, it could also explain the error saying future not complete
Future<Stations> fetchStations() async {
stations = await API_Call().fetchStations();
for (Station stations in stations) {
GeoCoordinates geoCoordinates = GeoCoordinates (stations.place.location.lat, stations.place.location.lng);
// use the coords .. to add a marker
_addPOIMapMarker(geoCoordinates, 1);
}
// GeoCoordinates geoCoordinates = stations.coordinates;
// _addPOIMapMarker(geoCoordinates, 1);
}
}
You have to return a Stations object from it, try after your for loop something like return stations;, it could fix your problem, if the error changes, it's also a good start.
Also change your line in future builder to this:
if (snapshot.connectionState == ConnectionState.done && snapshot.hasData)
And for the meantime, remove this _setTapGestureHandler(). The crash is most likely caused by some memory leak, and from the code posted, it could be explained by listeners.

Flutter: Asking Location permission pauses all app execution, how to let it run asynchronously

When my app requests location permission the entire app stops until the dialog is complete (e.g. if the permission dialog pops up during a page transition the transition will freeze mid transition until the dialog is resolved).
Literally it causes execution to pause.
Using: flutter_riverpod, location.
The offending code:
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter_riverpod/all.dart';
import 'package:location/location.dart';
class MapApiBloc extends StateNotifier<MapApiState> {
// Instantiating a location service
static Location _location = Location();
// This will subscribe to the user's location
StreamSubscription<LocationData> _streamSubscription;
// Permissions stuff
bool _serviceEnabled;
PermissionStatus _permissionGranted;
// Initial (empty) state
static const MapApiState _initialState = MapApiState(userLocation: null);
MapApiBloc() : super(_initialState) {
init();
}
// This runs when you initialize the class
init() async {
// Checks if user toggled on location service
_serviceEnabled = await _location.serviceEnabled();
if (!_serviceEnabled) {
_serviceEnabled = await _location.requestService();
if (!_serviceEnabled) {
return;
}
}
// Asks for permission
_permissionGranted = await _location.hasPermission();
if (_permissionGranted == PermissionStatus.denied) {
_permissionGranted = await _location.requestPermission();
if (_permissionGranted != PermissionStatus.granted) {
return;
}
}
// Starts the subscription
_streamSubscription = _location.onLocationChanged.listen((event) {
state = MapApiState(userLocation: event);
});
}
#override
void dispose() {
_streamSubscription.cancel();
super.dispose();
}
}
class MapApiState {
final LocationData userLocation;
const MapApiState({#required this.userLocation});
}
final mapApiProvider = StateNotifierProvider<MapApiBloc>((ref) {
return MapApiBloc();
});
UI Code:
class ViewNearbyMapPage extends StatefulWidget {
#override
_ViewNearbyMapPageState createState() => _ViewNearbyMapPageState();
}
class _ViewNearbyMapPageState extends State<ViewNearbyMapPage> {
Completer<GoogleMapController> _controller = Completer();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Search Sellers")),
body: Consumer(
builder: (context, watch, child) {
var location = watch(mapApiProvider.state);
if (location.userLocation?.latitude == null) {
return Center(child: CircularProgressIndicator());
}
CameraPosition _myPosition = CameraPosition(
target: LatLng(location.userLocation.latitude,
location.userLocation.longitude),
zoom: 14.4746,
);
return GoogleMap(
initialCameraPosition: _myPosition,
onMapCreated: (controller) {
_controller.complete(controller);
},
);
},
),
);
}
}

How to Pick files and Images for upload with flutter web

I would like to know how to pick an Image from the users computer into my flutter web app for upload
Using dart:html package directly in Flutter is not recommended.
Instead, use this package: https://pub.dev/packages/file_picker.
Example of how to use in Flutter Web:
class FileUploadButton extends StatelessWidget {
#override
Widget build(BuildContext context) {
return RaisedButton(
child: Text('UPLOAD FILE'),
onPressed: () async {
var picked = await FilePicker.platform.pickFiles();
if (picked != null) {
print(picked.files.first.name);
}
},
);
}
}
Note that FilePickerResult.path is not supported in Flutter Web.
I tried the code below and it worked.
first import 'dart:html';
// variable to hold image to be displayed
Uint8List uploadedImage;
//method to load image and update `uploadedImage`
_startFilePicker() async {
InputElement uploadInput = FileUploadInputElement();
uploadInput.click();
uploadInput.onChange.listen((e) {
// read file content as dataURL
final files = uploadInput.files;
if (files.length == 1) {
final file = files[0];
FileReader reader = FileReader();
reader.onLoadEnd.listen((e) {
setState(() {
uploadedImage = reader.result;
});
});
reader.onError.listen((fileEvent) {
setState(() {
option1Text = "Some Error occured while reading the file";
});
});
reader.readAsArrayBuffer(file);
}
});
}
now just any Widget, like a button and call the method _startFilePicker()
import 'package:http/http.dart' as http;
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
class FileUploadWithHttp extends StatefulWidget {
#override
_FileUploadWithHttpState createState() => _FileUploadWithHttpState();
}
class _FileUploadWithHttpState extends State<FileUploadWithHttp> {
PlatformFile objFile = null;
void chooseFileUsingFilePicker() async {
//-----pick file by file picker,
var result = await FilePicker.platform.pickFiles(
withReadStream:
true, // this will return PlatformFile object with read stream
);
if (result != null) {
setState(() {
objFile = result.files.single;
});
}
}
void uploadSelectedFile() async {
//---Create http package multipart request object
final request = http.MultipartRequest(
"POST",
Uri.parse("Your API URL"),
);
//-----add other fields if needed
request.fields["id"] = "abc";
//-----add selected file with request
request.files.add(new http.MultipartFile(
"Your parameter name on server side", objFile.readStream, objFile.size,
filename: objFile.name));
//-------Send request
var resp = await request.send();
//------Read response
String result = await resp.stream.bytesToString();
//-------Your response
print(result);
}
#override
Widget build(BuildContext context) {
return Container(
child: Column(
children: [
//------Button to choose file using file picker plugin
RaisedButton(
child: Text("Choose File"),
onPressed: () => chooseFileUsingFilePicker()),
//------Show file name when file is selected
if (objFile != null) Text("File name : ${objFile.name}"),
//------Show file size when file is selected
if (objFile != null) Text("File size : ${objFile.size} bytes"),
//------Show upload utton when file is selected
RaisedButton(
child: Text("Upload"), onPressed: () => uploadSelectedFile()),
],
),
);
}
}
I've tested this package and was very happy with the result imagePickerWeb it returns 3 different types it can be in the form of Image(widget for preview), byte, File(upload)
then you can use this to get the values
html.File _cloudFile;
var _fileBytes;
Image _imageWidget;
Future<void> getMultipleImageInfos() async {
var mediaData = await ImagePickerWeb.getImageInfo;
String mimeType = mime(Path.basename(mediaData.fileName));
html.File mediaFile =
new html.File(mediaData.data, mediaData.fileName, {'type': mimeType});
if (mediaFile != null) {
setState(() {
_cloudFile = mediaFile;
_fileBytes = mediaData.data;
_imageWidget = Image.memory(mediaData.data);
});
}
Uploading to firebase
don't forget to add this to your index.html
<script src="https://www.gstatic.com/firebasejs/7.5.0/firebase-storage.js"></script>
Uploading to firebase
import 'package:firebase/firebase.dart' as fb;
uploadToFirebase(File file) async {
final filePath = 'temp/${DateTime.now()}.png';//path to save Storage
try {
fb
.storage()
.refFromURL('urlFromStorage')
.child(filePath)
.put(file);
} catch (e) {
print('error:$e');
}
}
See the documentation of the package if you still have problems
The accepted answer is indeed outdated. Like jnt suggested, https://pub.dev/packages/file_picker is a handy package, when it comes to implementing an image upload using Flutter Web.
The problem I was facing is to get a base64 representation of an image, since I was using it to store images in Firestore. As we know, dart:io is not supported on Flutter Web and throws Unsupported operation: _Namespace error. Hence, using File and reading file's bytes was not an option. Luckily, the package provides API to convert the uploaded image to Uint8List. Here is my implementation:
import 'package:file_picker/file_picker.dart';
...
FilePickerResult? pickedFile;
...
void chooseImage() async {
pickedFile = await FilePicker.platform.pickFiles();
if (pickedFile != null) {
try {
setState(() {
logoBase64 = pickedFile!.files.first.bytes;
});
} catch (err) {
print(err);
}
} else {
print('No Image Selected');
}
}
In case you need to display the local image right away, use Image.memory.
Image.memory(logoBase64!);
i have this problem too;
i use https://pub.dev/packages/file_picker but in flutter web path not suppor;
you should to use bytes;
i save file bytes in var _fileBytes and use in request;
var request = http.MultipartRequest('POST', Uri.parse('https://.....com'));
request.headers.addAll(headers);
request.files.add(
http.MultipartFile.fromBytes(
'image',
await ConvertFileToCast(_fileBytes),
filename: fileName,
contentType: MediaType('*', '*')
)
);
request.fields.addAll(fields);
var response = await request.send();
function ConvertFileToCast:
ConvertFileToCast(data){
List<int> list = data.cast();
return list;
}
it`s work for me :)
if anyone is wondering how to get it working on mobile and web :
var bytes;
await file!.files.first.readStream!
.map((asciiValue) => bytes = asciiValue)
.toList();
FormData body;
final MultipartFile file = MultipartFile.fromBytes(bytes, filename: "file");
MapEntry<String, MultipartFile> imageEntry = MapEntry("image", file);
body.files.add(imageEntry);
I can share the way I upload image to AWS s3 from flutter web recently.
May not exact match the case who is looking for answer here but I think it may inpired others somehow.
First I try to use amplify_storage_s3 package but it not support for Flutter Web yet for now. So I use basic http post instead.
The packages I use:
file_picker: For web, FileUploadInputElement (from html package) may do the same thing but I think using this package can make thing simpler.
dio: I'm not sure why I cannot use http's MultipartFile successfully so I use this instead. (maybe someone can provide a version using http package)
mine: transfer extension to mimetype
Code example:
import 'package:flutter/material.dart';
import 'package:dio/dio.dart' as dio;
import 'package:file_picker/file_picker.dart';
import 'package:mime/mime.dart';
class FileUploader extends StatelessWidget {
const FileUploader({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: FloatingActionButton(
onPressed: () async {
// 1. Pick an image file
final filePicked = await FilePicker.platform.pickFiles();
if (filePicked != null) {
final file = filePicked.files.single; // PlatformFile
final mimeType = lookupMimeType(file.name) ?? '';
/// 2. Get presigned data somewhere
const url = 'https://s3.amazonaws.com/......';
final fields = {
'bucket': '...',
'X-Amz-Algorithm': 'AWS4-HMAC-SHA256',
'X-Amz-Credential': '...',
'X-Amz-Date': '...',
'Policy': '...',
'X-Amz-Signature': '...',
'x-amz-meta-userid': '...',
'Content-Type': mimeType,
'file': dio.MultipartFile.fromBytes(file.bytes ?? []),
};
/// 3. Send file to AWS s3
final formData = dio.FormData.fromMap(fields);
await dio.Dio().post(url, data: formData);
}
},
child: const Icon(Icons.upload),
),
);
}
}
Here is my working code to upload using dio. I use dio because it has a callback progress function.
class _FileUploadViewState extends State<FileUploadView> {
#override
void initState() {
super.initState();
}
FilePickerResult? result;
PlatformFile? file;
Response? response;
String? progress;
String? percentage;
Dio dio = Dio();
selectFile() async {
result =
await FilePicker.platform.pickFiles(type: FileType.any, withData: true);
if (result != null) {
file = result?.files.single;
}
//print(file?.name);
//print(file?.bytes?.length);
//print(file?.size);
//print(file?.extension);
//print(file?.path);
setState(() {});
}
Future<void> uploadFile(BuildContext context, User user) async {
final navigator = Navigator.of(context);
final storage = FlutterSecureStorage();
String? token = await storage.read(key: 'jwt');
final formData = FormData.fromMap(
{
'file': MultipartFile.fromBytes(file?.bytes as List<int>,
filename: file?.name)
},
);
dio.options.headers['content-Type'] = 'application/octet-stream';
dio.options.headers["authorization"] = "Bearer $token";
response = await dio.post(
user.fileUrl,
data: formData,
onSendProgress: (int sent, int total) {
percentage = (sent / total * 100).toStringAsFixed(2);
progress = "$sent Bytes of $total Bytes - $percentage % uploaded";
setState(
() {},
);
},
);
if (response!.statusCode == 200) {
....
My go code for the server looks like this,
if err := r.ParseMultipartForm(64 << 20); err != nil {
log.Println("error processing multipart form")
log.Println(err)
http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
return
}
file, handler, err := r.FormFile("file")

Flutter Text To Speech: Speak several strings subsequently with different SpeechRates

I have a problem with the Flutter Text To Speech package.
When clicking on a FloatingActionButton I would like to speak/play several Strings (with different Speechrates) subsequently. However, when doing so, I can only hear the last string that I have passed onto the function and not the first one.
As you can see in the code below, I have tried to make use of the asynchronus programming (async / await).
import 'package:flutter/material.dart';
import 'dart:async';
import 'package:flutter_tts/flutter_tts.dart';
class SpeakerClass extends StatefulWidget{
#override
State<StatefulWidget> createState() {
// TODO: implement createState
return _SpeakerClassState();
}
}
class _SpeakerClassState extends State<SpeakerClass>{
String text1 = 'eins';
String text2 = 'zwei';
String text3 = 'drei';
String text4 = 'vier';
String currentTtsString;
double ttsSpeechRate1 = 0.5;
double ttsSpeechRate2 = 1.0;
double currentSpeechRate;
Future playTtsString1() async {
currentTtsString = text1;
currentSpeechRate = ttsSpeechRate1;
await runTextToSpeech(currentTtsString, currentSpeechRate);
return null;
}
Future playTtsString2() async {
currentTtsString = text2;
currentSpeechRate = ttsSpeechRate2;
await runTextToSpeech(currentTtsString, currentSpeechRate);
return null;
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: FloatingActionButton (
backgroundColor: Colors.blue,
child: Icon(Icons.volume_up, color: Colors.white),
onPressed: () async {
await playTtsString1();
await playTtsString2();
},
)
);
}
}
Future<void> runTextToSpeech(String currentTtsString, double currentSpeechRate) async {
FlutterTts flutterTts;
flutterTts = new FlutterTts();
await flutterTts.setLanguage("en-GB");
await flutterTts.setVolume(1.0);
await flutterTts.setPitch(1.0);
await flutterTts.isLanguageAvailable("en-GB");
await flutterTts.setSpeechRate(currentSpeechRate);
await flutterTts.speak(currentTtsString);
}
When pressing the FloatingActionButton I expect the program to first carry out the function playTtsString1 ("eins" with a speed of 0.5) and afterwards the function playTtsString2 ("zwei" with a speed of 1).
However, somehow I can only hear the program saying "zwei". I guess the program is not waiting for the first function "playTtsString1" to be finished and already carries out the second function "playTtsString2". I would really appreciate any help on this matter!
import 'package:flutter/material.dart';
import 'dart:async';
import 'package:flutter_tts/flutter_tts.dart';
class SpeakerClass extends StatefulWidget{
#override
State<StatefulWidget> createState() {
// TODO: implement createState
return _SpeakerClassState();
}
}
class _SpeakerClassState extends State<SpeakerClass>{
String text1 = 'eins';
String text2 = 'zwei';
String text3 = 'drei';
String text4 = 'vier';
String currentTtsString;
double ttsSpeechRate1 = 0.5;
double ttsSpeechRate2 = 1.0;
double currentSpeechRate;
FlutterTts flutterTts;
bool bolSpeaking = false;
Future playTtsString1() async {
bolSpeaking = true;
currentTtsString = text1;
currentSpeechRate = ttsSpeechRate1;
await runTextToSpeech(currentTtsString, currentSpeechRate);
return null;
}
Future playTtsString2() async {
bolSpeaking = true;
currentTtsString = text2;
currentSpeechRate = ttsSpeechRate2;
await runTextToSpeech(currentTtsString, currentSpeechRate);
return null;
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: FloatingActionButton (
backgroundColor: Colors.blue,
child: Icon(Icons.volume_up, color: Colors.white),
onPressed: () async {
// Play String 1
await playTtsString1();
// Check String 1 Finish
new Future.delayed(new Duration(milliseconds: 100), () async {
// loop until bolSpeaking = false
while (bolSpeaking) {
await Thread.sleep(100);
}
// play String 2
await playTtsString2();
}
},
)
);
}
}
Future<void> runTextToSpeech(String currentTtsString, double currentSpeechRate) async {
flutterTts = new FlutterTts();
await flutterTts.setLanguage("en-GB");
await flutterTts.setVolume(1.0);
await flutterTts.setPitch(1.0);
await flutterTts.isLanguageAvailable("en-GB");
await flutterTts.setSpeechRate(currentSpeechRate);
flutterTts.setCompletionHandler(() {
setState(() {
// The following code(s) will be called when the TTS finishes speaking
bolSpeaking = false;
});
});
flutterTts.speak(currentTtsString);
}
This should now work with the latest flutter_tts version.
You simply need to set awaitSpeakCompletion before the speaking happens.
You can update your run method like so:
Future<void> runTextToSpeech(String currentTtsString, double currentSpeechRate) async {
FlutterTts flutterTts;
flutterTts = new FlutterTts();
await flutterTts.awaitSpeakCompletion(true);
await flutterTts.setLanguage("en-GB");
await flutterTts.setVolume(1.0);
await flutterTts.setPitch(1.0);
await flutterTts.isLanguageAvailable("en-GB");
await flutterTts.setSpeechRate(currentSpeechRate);
await flutterTts.speak(currentTtsString);
}

Flutter: show only special part of an image

This is how I show a picture:
return Scaffold(
body: Center(
child: Image.asset('man_face.jpg'),
),
);
And this the result: https://imgur.com/a/CPrQgvS
I want to show only special part of the picture. For example a rectangle with x: 250 and y: 360 and width: 200 and height: 150.
Which it should be something like this: https://imgur.com/a/p41y3nx
How can I do that?
you might want to look into this library. brendan-duncan/image. a great tool to manipulate images in flutter.
Here is a code I came up with
it accept an image url and a rect and display only the rect part of the image
import 'dart:async';
import 'package:flutter/material.dart';
import 'dart:ui' as ui;
class PartImagePainter extends StatefulWidget {
String imageUrl;
Rect rect;
PartImagePainter(this.imageUrl, this.rect);
#override
_PartImagePainterState createState() => _PartImagePainterState();
}
class _PartImagePainterState extends State<PartImagePainter> {
Future<ui.Image> getImage(String path) async {
Completer<ImageInfo> completer = Completer();
var img = new NetworkImage(path);
img
.resolve(ImageConfiguration())
.addListener(ImageStreamListener((ImageInfo info, bool _) {
completer.complete(info);
}));
ImageInfo imageInfo = await completer.future;
return imageInfo.image;
}
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: getImage(widget.imageUrl),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
// If the Future is complete, display the preview.
return paintImage(snapshot.data);
} else {
// Otherwise, display a loading indicator.
return Center(child: CircularProgressIndicator());
}
});
}
paintImage(image) {
return CustomPaint(
painter: ImagePainter(image, widget.rect),
child: SizedBox(
width: MediaQuery.of(context).size.width,
height: widget.rect.height,
),
);
}
}
class ImagePainter extends CustomPainter {
ui.Image resImage;
Rect rectCrop;
ImagePainter(this.resImage, this.rectCrop);
#override
void paint(Canvas canvas, Size size) {
if (resImage == null) {
return;
}
final Rect rect = Offset.zero & size;
final Size imageSize =
Size(resImage.width.toDouble(), resImage.height.toDouble());
FittedSizes sizes = applyBoxFit(BoxFit.fitWidth, imageSize, size);
Rect inputSubRect = rectCrop;
final Rect outputSubRect =
Alignment.center.inscribe(sizes.destination, rect);
final paint = Paint()
..color = Colors.white
..style = PaintingStyle.fill
..strokeWidth = 4;
canvas.drawRect(rect, paint);
canvas.drawImageRect(resImage, inputSubRect, outputSubRect, Paint());
}
#override
bool shouldRepaint(CustomPainter oldDelegate) {
return false;
}
}
try using Alignment property and set fit to none.
Image.network(
"your image.png",
// move on the X axis to right 10% of the image and 0% on the Y Axis
alignment: const Alignment(0.1,0),
// set fit to none
fit: BoxFit.none,
// use scale to zoom out of the image
scale: 2,
)
One way suggested by the official doc of Flutter, is to:
To display a subpart of an image, consider using a CustomPainter and Canvas.drawImageRect.
ref: https://api.flutter.dev/flutter/painting/DecorationImage/alignment.html
Thus here is my full code. Use PartImage to show what you want.
class PartImage extends StatefulWidget {
const PartImage({
Key key,
#required this.imageProvider,
#required this.transform,
}) : assert(imageProvider != null),
super(key: key);
final ImageProvider imageProvider;
final Matrix4 transform;
#override
_PartImageState createState() => _PartImageState();
}
class _PartImageState extends State<PartImage> {
ImageStream _imageStream;
ImageInfo _imageInfo;
#override
void didChangeDependencies() {
super.didChangeDependencies();
_getImage();
}
#override
void didUpdateWidget(PartImage oldWidget) {
super.didUpdateWidget(oldWidget);
if (widget.imageProvider != oldWidget.imageProvider) _getImage();
}
void _getImage() {
final oldImageStream = _imageStream;
_imageStream = widget.imageProvider.resolve(createLocalImageConfiguration(context));
if (_imageStream.key != oldImageStream?.key) {
final listener = ImageStreamListener(_updateImage);
oldImageStream?.removeListener(listener);
_imageStream.addListener(listener);
}
}
void _updateImage(ImageInfo imageInfo, bool synchronousCall) {
setState(() {
_imageInfo = imageInfo;
});
}
#override
void dispose() {
_imageStream.removeListener(ImageStreamListener(_updateImage));
super.dispose();
}
#override
Widget build(BuildContext context) {
return RawPartImage(
image: _imageInfo?.image, // this is a dart:ui Image object
scale: _imageInfo?.scale ?? 1.0,
transform: widget.transform,
);
}
}
/// ref: [RawImage]
class RawPartImage extends StatelessWidget {
final ui.Image image;
final double scale;
final Matrix4 transform;
const RawPartImage({Key key, this.image, this.scale, this.transform}) : super(key: key);
#override
Widget build(BuildContext context) {
return CustomPaint(
painter: _RawPartImagePainter(
image: image,
scale: scale,
transform: transform,
),
);
}
}
class _RawPartImagePainter extends CustomPainter {
final ui.Image image;
final double scale;
final Matrix4 transform;
final painter = Paint();
_RawPartImagePainter({this.image, this.scale, this.transform});
#override
void paint(Canvas canvas, Size size) {
if (image == null) {
return;
}
final transformInv = Matrix4.inverted(transform);
final dst = Offset.zero & size;
final src = Rect.fromPoints(
transformOffset(transformInv, dst.topLeft),
transformOffset(transformInv, dst.bottomRight),
);
// print('src=$src dst=$dst');
canvas.drawImageRect(image, src, dst, painter);
}
#override
bool shouldRepaint(covariant _RawPartImagePainter oldDelegate) {
return oldDelegate.image != image || //
oldDelegate.scale != scale ||
oldDelegate.transform != transform;
}
}
Offset transformOffset(Matrix4 transform, Offset offset) {
Vector4 vecOut = transform * Vector4(offset.dx, offset.dy, 0, 1);
return Offset(vecOut.x, vecOut.y);
}
By the way, if you are interested in knowing what happens behind drawImageRect:
Have a search https://github.com/flutter/engine/search?q=drawImageRect
This seems the C++ code that drawImageRect (i.e. _drawImageRect actually calls: https://github.com/flutter/engine/blob/6bc70e4a114ff4c01b60c77bae754bace5683f6d/lib/ui/painting/canvas.cc#L330
It calls canvas_->drawImageRect. What is canvas_? From the header file we see it is of type SkCanvas* canvas_;.
Then we go into the world of Skia (not Flutter or Dart anymore). https://skia.org/user/api/skcanvas_overview for an overview of SkCanvas. And https://api.skia.org/classSkCanvas.html#a680ab85c3c7b5eab23b853b97f914334 for the actual SkCanvas.drawImageRect documentation.

Resources