Hi the flutter code below loads a photo from the camera roll and then displays it, on video, what I have to do is recover the path of the file, to do it I use the code below inside the inserimento function but when I run the code I the following error:
Try correcting the name to the name of an existing getter, or defining
a getter or field named 'path'. print("\n Immagine:
"+imageFile.path);
Flutter Code:
Future<File> imageFile;
//Costruttore
ArticoloEditPage(){
aggiornaValori();
BackButtonInterceptor.add(myInterceptor);
setData(new DateTime.now());
}
//Disabilito il bottone di back su android
bool myInterceptor(bool stopDefaultButtonEvent, RouteInfo info) {
return true;
}
//Funzione di init
void init() {
aggiornaValori();
BackButtonInterceptor.add(myInterceptor);
}
//Funzione che esegue la creazione dell'utente
Future<bool> inserimento(BuildContext context) async {
print("\n Immagine: "+imageFile.path);
// var base64File=await Supporto.castDocumentToBase64(imgPath);
//print("\n Immagine in base64: "+imgPath);
}
pickImageFromGallery(ImageSource source) {
setState(() {
imageFile = ImagePicker.pickImage(source: source);
});
}
Widget showImage() {
return FutureBuilder<File>(
future: imageFile,
builder: (BuildContext context, AsyncSnapshot<File> snapshot) {
if (snapshot.connectionState == ConnectionState.done &&
snapshot.data != null) {
return Image.file(
snapshot.data,
width: 300,
height: 300,
);
} else if (snapshot.error != null) {
return const Text(
'Errore caricamento non riuscito',
textAlign: TextAlign.center,
);
} else {
return const Text(
'Nessuna immagine selezionata',
textAlign: TextAlign.center,
);
}
},
);
}
you can not get path directly in future method,
so
by
1. this you can print your path.
Future<bool> inserimento(BuildContext context) async {
var pathData=await imageFile;
print("\n Immagine: "+pathData.path);
}
or
2. if you need path in widget
Widget showImage() {
return FutureBuilder<File>(
future: imageFile,
builder: (BuildContext context, AsyncSnapshot<File> snapshot) {
if (snapshot.connectionState == ConnectionState.done &&
snapshot.data != null) {
print("Your Path : "+snapshot.data.path);
return Image.file(
snapshot.data,
width: 300,
height: 300,
);
} else if (snapshot.error != null) {
return const Text(
'Errore caricamento non riuscito',
textAlign: TextAlign.center,
);
} else {
return const Text(
'Nessuna immagine selezionata',
textAlign: TextAlign.center,
);
}
},
);
}
also
3. if you need base64 image.
then
Future<bool> inserimento(BuildContext context) async {
var pathData=await imageFile;
var base64Image = base64Encode(pathData.readAsBytesSync());
print("\n Immagine base64Image: "+base64Image.toString());
}
Related
I'm trying to create a basic Hero animation between a gallery grid view page (using photo manager plugin) and a detail page. When the hero animation is done both back and forward, the picture is flickering .
Here is the example I've made :
The full code runnable :
import 'dart:io';
import 'dart:typed_data';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:photo_manager/photo_manager.dart';
void main() {
runApp(const TestApp());
}
class TestApp extends StatefulWidget {
const TestApp({Key? key}) : super(key: key);
#override
State<TestApp> createState() => _TestAppState();
}
class _TestAppState extends State<TestApp> {
final List<AssetEntity> assetsList = [];
bool granted = false;
void loadAssets() async {
granted = await PhotoManager.requestPermission();
if (granted) {
FilterOptionGroup option = FilterOptionGroup()
..addOrderOption(const OrderOption(
type: OrderOptionType.createDate,
asc: false,
));
final albums = await PhotoManager.getAssetPathList(filterOption: option, type: RequestType.image);
print("albums : $albums");
if (albums.isNotEmpty) {
var alb = albums.where((element) {
return element.name == 'Test';
});
var album = alb.first;
// Now that we got the album, fetch all the assets it contains
List<AssetEntity> currentList =
await album.getAssetListRange(start: 0, end: 200);
// Update the state and notify UI
assetsList.clear();
assetsList.addAll(currentList);
}
setState(() {});
}
}
#override
void initState() {
loadAssets();
super.initState();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: granted
? _gridView()
: Center(
child: Container(
color: Colors.blue,
width: 200,
height: 200,
child: TextButton(
onPressed: () async {
granted = await PhotoManager.requestPermission();
setState(() {});
},
child: const Text(
"Ask permission",
style: TextStyle(color: Colors.white),
),
)),
)),
);
}
Widget _gridView() {
return GridView.builder(
itemCount: assetsList.length,
gridDelegate:
const SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3),
itemBuilder: (context, index) {
return Hero(
createRectTween: (Rect? begin, Rect? end) {
RectTween _rectTween = RectTween(begin: begin, end: end);
return _rectTween;
},
tag: assetsList[index].id,
child: GalleryThumbnail(
asset: assetsList[index],
onTap: (bytes) {
Navigator.push(context, MaterialPageRoute(builder: (context) {
return DetailsPage(asset: assetsList[index], bytes: bytes);
}));
},
));
});
}
}
class GalleryThumbnail extends StatelessWidget {
final AssetEntity asset;
final ValueChanged<Uint8List> onTap;
const GalleryThumbnail({Key? key, required this.asset, required this.onTap})
: super(key: key);
#override
Widget build(BuildContext context) {
return FutureBuilder<Uint8List?>(
future: Platform.isIOS
? asset.thumbDataWithOption(
ThumbOption.ios(
width: 500,
height: 500,
deliveryMode: DeliveryMode.opportunistic,
resizeMode: ResizeMode.fast,
resizeContentMode: ResizeContentMode.fit,
quality: 100
// resizeContentMode: ResizeContentMode.fill,
),
)
: asset.thumbDataWithSize(250, 250),
builder: (_, snapshot) {
final bytes = snapshot.data;
if (snapshot.hasError) {
return Container();
}
// If we have no data
if (bytes == null) return Container();
// If there's data, display it as an image
return GestureDetector(
onTap: () {
onTap(bytes);
},
child: Image.memory(bytes, fit: BoxFit.cover,gaplessPlayback: true,));
},
);
}
}
class DetailsPage extends StatefulWidget {
final AssetEntity asset;
final Uint8List bytes;
const DetailsPage({Key? key, required this.asset, required this.bytes})
: super(key: key);
#override
_DetailsPageState createState() => _DetailsPageState();
}
class _DetailsPageState extends State<DetailsPage> {
late ImageProvider _imageProvider;
Future<void> loadFile() async {
try {
File? file = await widget.asset.file;
if (file == null) return;
_imageProvider = Image.file(file).image;
setState(() {});
} catch (e) {
print("error to load file : " + e.toString());
}
}
#override
void initState() {
_imageProvider = Image.memory(widget.bytes).image;
loadFile();
super.initState();
}
#override
Widget build(BuildContext context) {
double ratio = widget.asset.height / widget.asset.width;
return Scaffold(
body: Stack(
children: [
Hero(
createRectTween: (Rect? begin, Rect? end) {
RectTween _rectTween = RectTween(begin: begin, end: end);
return _rectTween;
},
tag: widget.asset.id,
child: Center(
child: Image(image: _imageProvider,
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height*ratio,
gaplessPlayback: true),
),
),
const SafeArea(
child: Padding(
padding: EdgeInsets.all(8.0),
child: BackButton(),
)),
],
),
);
}
}
I've implemented hero animation somewhere else in my project and I'm not getting this behavior where as here I do.
Why this is happening and how to correct this ?
Add
transitionOnUserGestures: true,
to Hero() widget in current & next page
This happens because the Hero of the image thumbail wraps the FutureBuilder, and then the future triggers again during the animation, wraps the Image instead of the FutureBuilder.
I cannot display selected images from gallery in a grid. In this code, I am displaying images in a list and I want to turn it into small grid type in 1 row but I don't know how. Can you please help?
Here's my code for selecting multiple images using file picker.
FileType fileType;
String imgName, _imgPath;
Map<String, String> imgPaths;
List<File> _imgList = List();
bool isLoadingPath = false;
_openFile() async {
setState(() => isLoadingPath = true);
try {
_imgPath = null;
imgPaths = await FilePicker.getMultiFilePath(
type: fileType != null ? fileType : FileType.custom,
allowedExtensions: ['jpg', 'png']);
_imgList.clear();
imgPaths.forEach((key, val) {
print('{ key: $key, value: $val}');
File file = File(val);
_imgList.add(file);
});
} on PlatformException catch (e) {
print("Unsupported operation" + e.toString());
}
if (!mounted) return;
setState(() {
isLoadingPath = false;
imgName = _imgPath != null
? _imgPath.split('/').last
: imgPaths != null
? imgPaths.keys.toString()
: '...';
});
}
Displaying images in a list. (How to display images as it is?)
Widget _fileBuilder() {
return Builder(
builder: (BuildContext context) => isLoadingPath
? Padding(
padding: const EdgeInsets.only(bottom: 4.0))
: _imgPath != null || imgPaths != null && (imgPaths.length > 1 && imgPaths.length < 5)
? new Container(
height: imgPaths.length > 1
? MediaQuery.of(context).size.height * 0.15
: MediaQuery.of(context).size.height * 0.10,
width: MediaQuery.of(context).size.width,
child: new Scrollbar(
child: new ListView.separated(
itemCount: imgPaths != null && imgPaths.isNotEmpty
? imgPaths.length
: 1,
itemBuilder: (BuildContext context, int index) {
final bool isMultiPath = imgPaths != null && imgPaths.isNotEmpty;
final int fileNo = index + 1;
final String name = 'File $fileNo : ' + (isMultiPath
? imgPaths.keys.toList()[index]
: _imgPath ?? '...');
final filePath = isMultiPath
? imgPaths.values.toList()[index].toString()
: _imgPath;
return new ListTile(
title: Transform.translate(
offset: Offset(-25, 0),
child: new Text(
name,
),
),
leading: Icon(Icons.attach_file_outlined, color: Color(0xFFF3A494),),
dense: true,
);
},
separatorBuilder:
(BuildContext context, int index) =>
new Divider(),
)),
)
: new Container(child: Text('4 photos is the maximum'),),
);
}
Dependencies:
file_picker: ^1.4.2
path:
mime:
async:
what you can do is, use the Image Picker dependency. You can find its documentation on pub.dev. after installing it, try using it and store the image uploaded in a file in the device. and with that file name, you can access the image.
You can try the below code, it worked for me. Also don't forget to import dart:io; for using file.
var _storedImage;
Future<void> _takePictureByCamera() async {
final picker = ImagePicker();
final imageFile =
await picker.getImage(source: ImageSource.camera, maxWidth: 600, imageQuality: 60);
setState(() {
_storedImage = File(imageFile!.path);
});
final appDir = await path_provider.getApplicationDocumentsDirectory();
final fileName = path.basename(imageFile!.path);
final savedImage = File(imageFile.path).copy('${appDir.path}/$fileName');
widget.onSelectImage(savedImage);
}
Future<void> _takePictureByGallery() async {
final picker = ImagePicker();
final imageFile =
await picker.getImage(source: ImageSource.gallery, maxWidth: 600);
if (imageFile == null) {
return;
}
setState(() {
_storedImage = File(imageFile.path);
});
final appDir = await path_provider.getApplicationDocumentsDirectory();
final fileName = path.basename(imageFile.path);
final savedImage = File(imageFile.path).copy('${appDir.path}/$fileName');
widget.onSelectImage(savedImage);
}
and after selecting or clicking the image, you can do this to display the image ->
void getImage() async {
final pickedImage = await showModalBottomSheet(
context: accountTabScaffoldMessengerKey.currentContext!,
backgroundColor: Colors.transparent,
enableDrag: true,
// elevation: 0,
builder: (context) => AccountImageUpdateBottomSheet(_selectImage),
);
_selectImage(pickedImage);
}
void _selectImage(File pickedImage) {
setState(() {
_pickedImage = pickedImage;
});
}
The image you selected is stored in the _pickedImage and you can access it by Image.file(_pickedImage).
I am using version 0.6.7+22 of the image_picker Flutter package to pick an image from the device in my Flutter web app. I call getImage function in a pop-up:
class _ImageVerificationPopUpState extends State<ImageVerificationPopUp> {
File _image;
final picker = ImagePicker();
#override
Widget build(BuildContext context) {
return AlertDialog(
title: Text("Upload screenshot"),
content: SizedBox(
width: MediaQuery.of(context).size.width * 0.3,
child: Center(
child: _image != null
? SelectedImage(kIsWeb ? Image.network(_image.path) : Image.file(_image), () {
setState(() {
_image = null;
});
})
: SelectImageButton(_getImage),
),
),
actions: [
TextButton(onPressed: () => Navigator.of(context).pop(), child: Text("Cancel")),
TextButton(
onPressed: () {
final ImageCubit imageCubit = BlocProvider.of<imageCubit>(context);
imageCubit.uploadImage(_image);
Navigator.pop(context);
},
child: Text("Upload"))
],
backgroundColor: Color(0xFF333D81),
);
}
Future<void> _getImage() async {
final PickedFile pickedFile = await picker.getImage(source: ImageSource.gallery);
setState(() {
if (pickedFile != null) {
_image = File(pickedFile.path);
} else {
print("No image selected");
}
});
}
}
After I press the button, it throws the following error:
Error: MissingPluginException(No implementation found for method pickImage on channel plugins.flutter.io/image_picker)
at Object.throw_ [as throw] (http://localhost:7357/dart_sdk.js:5331:11)
at platform_channel.MethodChannel.new._invokeMethod (http://localhost:7357/packages/flutter/src/services/system_channels.dart.lib.js:954:21)
at _invokeMethod.next (<anonymous>)
at http://localhost:7357/dart_sdk.js:39029:33
at _RootZone.runUnary (http://localhost:7357/dart_sdk.js:38886:58)
at _FutureListener.thenAwait.handleValue (http://localhost:7357/dart_sdk.js:33872:29)
at handleValueCallback (http://localhost:7357/dart_sdk.js:34432:49)
at Function._propagateToListeners (http://localhost:7357/dart_sdk.js:34470:17)
at _Future.new.[_completeWithValue] (http://localhost:7357/dart_sdk.js:34312:23)
at async._AsyncCallbackEntry.new.callback (http://localhost:7357/dart_sdk.js:34335:35)
at Object._microtaskLoop (http://localhost:7357/dart_sdk.js:39173:13)
at _startMicrotaskLoop (http://localhost:7357/dart_sdk.js:39179:13)
at http://localhost:7357/dart_sdk.js:34686:9
I already tried calling flutter clean, flutter pub get and rerunning my app, but it didn't help.
How can I solve this issue?
Thanks for your help in advance!
Try adding the package image_picker_for_web to your pubspec.yaml file.
...
dependencies:
...
image_picker: ^0.6.7
image_picker_for_web: ^0.1.0
...
...
then modify your _getImage method:
Future<void> _getImage() async {
final PickedFile pickedFile = await picker.getImage(source: ImageSource.gallery);
setState(() {
if (pickedFile != null) {
if (kIsWeb) { // Check if this is a browser session
_image = Image.network(pickedFile.path);
} else {
_image = Image.file(File(pickedFile.path));
}
} else {
print("No image selected");
}
});
}
I think this is what made it work for me. I was getting the same error message as you.
I'm new to flutter am developing contacts app using laravel backend i have two tables which are constrained on foreign key and json response works on postman. in Flutter i have two models users model and userdetail model. users name in list view like below and works fine
class ListUsers extends StatefulWidget {
#override
_ListItemsState createState() => _ListItemsState();
}
class _ListUsersState extends State<ListUsers> {
UsersApi usersApi;
#override
void initState() {
super.initState();
usersApi = UsersApi();
usersApi.fetchAllUsers();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('My Contacts'),
),
body: Container(
padding: EdgeInsets.all(16),
child:FutureBuilder(
future: itemsApi.fetchAllUsers(),
builder: (BuildContext context, AsyncSnapshot<List<Users>> snapshot)
{
switch(snapshot.connectionState)
{
case ConnectionState.active:
// working
return _loading();
break;
case ConnectionState.waiting:
//working
return _loading();
break;
case ConnectionState.none:
// no connection
return _error('No connection has been made');
break;
case ConnectionState.done:
// completed
if(snapshot.hasError)
{
return _error(snapshot.error.toString());
}
if(snapshot.hasData)
{
return _drawItemsList(snapshot.data,context);
}
break;
}
return Container();
},
),
),
);
}
Widget _drawItemsList(List<Users> myusers,BuildContext context)
{
return ListView.builder(
itemCount: myusers.length,
itemBuilder: (BuildContext context, int position){
return InkWell(
child:Card(
child:Padding(
padding: const EdgeInsets.all(8.0),
child: Text(musers[position].myuser_name),
) ,
),
onTap: (){
Navigator.push(context, MaterialPageRoute(builder: (context)=>UserDetails(muysers[position].id)));
},
);
},
);
}
Widget _loading()
{
return Container(
child: Center(
child: CircularProgressIndicator(),
),
);
}
Widget _error(String error)
{
return Container(
child: Center(
child: Text('Something went wrong'
),
),
);
}
}
my Api call to load listView is here also works fine
class UsersApi {
Future<List<Users>> fetchAllUsers() async //
{
String allUsers = CallApi.url + CallApi.items_url;
Map<String, String> headers =
{
'Accept': 'application/json',
};
var response = await http.get(allUsers, headers: headers);
List<Users> users = []; // its empty list we add it below
if (response.statusCode == 200) {
Map<String, dynamic> body = jsonDecode(response.body);
for (var item in body['data']) {
Users myUsers = Users.fromJson(item);
items.add(myUsers);
}
}
return users;
}
}
Now the error happens remember by passing single id from the first screen makes request of
CallApi.url+CallApi.My_Contacts+itemId;
to second screen and loads details of clicked info.
for loading other information of user from another table i created model and here is my request for second screen
class UserDetailApi {
Future<List<UserDetailModel>> fetchDetailOfSingleUser(String itemId) async
{
String userDetailUrl = CallApi.url+CallApi.My_Contacts+itemId;
Map<String,String> headers =
{
'Accept' : 'application/json',
};
var response = await http.get(userDetailUrl ,headers:headers);
List<UserDetailModel> user =[];
if(response.statusCode == 200)
{
Map<String, dynamic> body = jsonDecode(response.body);
for(var item in body['data'])
{
UserDetailModel userr = UserDetailModel.fromJson(item);
user.add(userr);
}
}
return user;
}
}
here is second activity
class UserDetails extends StatefulWidget
{
final String uId;
UserDetails(this.uId);
#override
_UserDetailsState createState()=>_UserDetailsState();
}
class _UserDetailsState extends State<UserDetails>
{
UserDetailApi itemsApi = UserDetailApi();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar:AppBar(
title:Text('Detailed Info'),
),
body: Container(
padding: EdgeInsets.all(16),
child: FutureBuilder(
future: itemsApi.fetchDetailOfSingleUser(widget.uId),
builder: (BuildContext context, AsyncSnapshot<List<UserDetailModel>> snapshot)
{
switch(snapshot.connectionState)
{
case ConnectionState.active:
// working
return _loading();
break;
case ConnectionState.waiting:
//working
return _loading();
break;
case ConnectionState.none:
// no connection
return _error('No connection has been made');
break;
case ConnectionState.done:
// completed
if(snapshot.hasError)
{
//return _error(snapshot.error.toString());
}
if(snapshot.hasData)
{
return _drawItemsList(snapshot.data);
}
break;
}
return Container();
},
),
),
);
}
Widget _drawItemsList(List<UserDetailModel> mdetail)
{
return ListView.builder(
itemCount: mdetail.length,
itemBuilder: (BuildContext context, int position){
return Padding(
padding: const EdgeInsets.all(8.0),
child: ListView.builder(
itemCount: mdetail.length,
itemBuilder: (BuildContext context, int position){
return InkWell(
child: Card(
child: Padding(
padding: EdgeInsets.all(8.0),
child: Text(here i need to display full user info from second table),
),
),
);
},
),
);
},
);
}
Widget _loading()
{
return Container(
child: Center(
child: CircularProgressIndicator(),
),
);
}
Widget _error(String error)
{
return Container(
child: Center(
child: Text('Something went wrong'
),
),
);
}
}
empty screen no results populated on TextBox so please tell me where i have missed or if my works are not correct for detailed activity thanks.
I want to add a simple image over an image from the gallery/camera. The behavior should be like it.
Open the application, choose a photo or make one with the floating buttons, then show the image and over put our logo on the bottom left. All it is working now.
Then I would like to save it both images in a single one, but I don't know how this process is called or how to achieve it.
import 'package:share/share.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Image Picker Demo',
home: MyHomePage(title: 'Image Picker Example'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
File _imageFile;
dynamic _pickImageError;
String _retrieveDataError;
void _onImageButtonPressed(ImageSource source) async {
try {
_imageFile = await ImagePicker.pickImage(source: source);
} catch (e) {
_pickImageError = e;
}
setState(() {});
}
Widget _previewImage() {
final Text retrieveError = _getRetrieveErrorWidget();
if (retrieveError != null) {
return retrieveError;
}
if (_imageFile != null) {
print('La imagen ha sido puesta en la pantalla?');
return Image.file(_imageFile);
} else if (_pickImageError != null) {
return Text(
'Pick image error: $_pickImageError',
textAlign: TextAlign.center,
);
} else {
return const Text(
'You have not yet picked an image.',
textAlign: TextAlign.center,
);
}
}
Future<void> retrieveLostData() async {
final LostDataResponse response = await ImagePicker.retrieveLostData();
if (response.isEmpty) {
return;
}
if (response.file != null) {
setState(() {
_imageFile = response.file;
});
} else {
_retrieveDataError = response.exception.code;
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Center(
child: Stack(children: <Widget>[
Container(
color: Colors.amber,
child: Platform.isAndroid
? FutureBuilder<void>(
future: retrieveLostData(),
builder:
(BuildContext context, AsyncSnapshot<void> snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
case ConnectionState.waiting:
return const Text(
'You have not yet picked an image.',
textAlign: TextAlign.center,
);
case ConnectionState.done:
return _previewImage();
default:
if (snapshot.hasError) {
return Text(
'Pick image/video error: ${snapshot.error}}',
textAlign: TextAlign.center,
);
} else {
return const Text(
'You have not yet picked an image.',
textAlign: TextAlign.center,
);
}
}
},
)
: (_previewImage())), //
Positioned(
bottom: 16,
left: 16,
width: 100,
height: 100,
child: Image.network(
'http://father-home.ru/wp-content/uploads/2018/05/cropped-logo8.png'))
]))),
floatingActionButton: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
FloatingActionButton(
onPressed: () {
_onImageButtonPressed(ImageSource.gallery);
},
heroTag: 'image0',
tooltip: 'Pick Image from gallery',
child: const Icon(Icons.photo_library),
),
Padding(
padding: const EdgeInsets.only(top: 16.0),
child: FloatingActionButton(
onPressed: () {
_onImageButtonPressed(ImageSource.camera);
},
heroTag: 'image1',
tooltip: 'Take a Photo',
child: const Icon(Icons.camera_alt),
),
),
Padding(
padding: const EdgeInsets.only(top: 16.0),
child: FloatingActionButton(
onPressed: () {
_onShare();
},
tooltip: 'Share',
child: const Icon(Icons.share),
),
),
],
),
);
}
void _onShare(){
//TODO: save the two images into one and share
}
Text _getRetrieveErrorWidget() {
if (_retrieveDataError != null) {
final Text result = Text(_retrieveDataError);
_retrieveDataError = null;
return result;
}
return null;
}
}
the widget with this code looks like it:
You can use RepaintBoundary widget to export a specific widget to image.
I believe that you will need to work with Bitmap.
Load 2 bitmaps, one is the background, one is your logo.
Apply transformation as you like.
Save output bitmap to file.
Take a look at: https://pub.dev/packages/bitmap