I'm trying to build an in app gallery function where I can screenshot certain widgets in the app and then display it inside the app's internal gallery screen. Currently, I am using the Flutter screenshot package and then I save the screenshot to the Application document directory path. But how do I retrieve the images back and display them later on the app again?
Here's how I've done it before using the path_provider package.
(this is only for one image, but you can adapt the code to retrieve multiple images).
Create an uninitialized variable:
Image? image;
then, in the initState:
void initState() {
_loadImage();
super.initState();
}
Here is the _loadImage()
Future<void> _loadImage() async {
final path = await imagePath;
final _doesTheImageExist = File('${path}/qr.png').existsSync();
print(_doesTheImageExist);
if (_doesTheImageExist == true) {
setState(() {
image = Image.file(File("${path}/qr.png"));
});
} else {
print('no');
setState(() {
image = null;
});
}
}
Create a getter to get the image path imagePath:
Future<String> get imagePath async {
final directory = (await getApplicationDocumentsDirectory()).path;
return '$directory/qr.png';
}
then, create a function to save the image to the device:
Future<void> _captureAndSaveQRCode() async {
final imageDirectory = await imagePath;
await _screenshotController.captureAndSave(imageDirectory,
fileName: 'qr.png');
await _loadImage();
// setState(() {});
}
Here is the complete runnable example:
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:path_provider/path_provider.dart';
import 'package:qr_flutter/qr_flutter.dart';
import 'package:screenshot/screenshot.dart';
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const Scaffold(
body: QrCode(),
),
);
}
}
class QrCode extends StatefulWidget {
const QrCode({Key? key}) : super(key: key);
#override
State<QrCode> createState() => _QrCodeState();
}
class _QrCodeState extends State<QrCode> {
final _screenshotController = ScreenshotController();
Image? image;
#override
Widget build(BuildContext context) {
return Scaffold(
body: image != null
? Center(
child: Padding(
padding: const EdgeInsets.only(
top: 100.0,
),
child: Column(
children: [
image!,
],
),
))
: Center(
child: Column(
children: [
TextButton(
onPressed: _captureAndSaveQRCode,
child: Text("Save QrCode")),
Screenshot(
controller: _screenshotController,
child: _buildQRImage('data'))
],
),
),
);
}
Widget _buildQRImage(String data) {
return QrImage(
data: data,
size: 250.0,
version: QrVersions.auto,
errorCorrectionLevel: QrErrorCorrectLevel.H,
gapless: false,
foregroundColor: Colors.black,
backgroundColor: Colors.white,
);
}
Future<String> get imagePath async {
final directory = (await getApplicationDocumentsDirectory()).path;
return '$directory/qr.png';
}
Future<void> _loadImage() async {
final path = await imagePath;
final _doesTheImageExist = File('${path}/qr.png').existsSync();
print(_doesTheImageExist);
if (_doesTheImageExist == true) {
setState(() {
image = Image.file(File("${path}/qr.png"));
});
} else {
setState(() {
image = null;
});
}
}
Future<void> _captureAndSaveQRCode() async {
final imageDirectory = await imagePath;
await _screenshotController.captureAndSave(imageDirectory,
fileName: 'qr.png');
await _loadImage();
}
#override
void initState() {
_loadImage();
super.initState();
}
}
Related
I hope you are well, I have a big problem with the flutter audio waveforms package, indeed I want to display the waveforms of my voice files in a folder, but they are not displayed and when I display the value of playerController.bufferData I get a null, please how can I manage this problem, I have already spent days on it.
Sincerely
below are my classes
import 'dart:async';
import 'dart:io';
import 'package:audio_recorder/AudioWaveFormsWidget.dart';
import 'package:audio_waveforms/audio_waveforms.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:path_provider/path_provider.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:record/record.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Audio Recorder',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Audio Recorder'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
//late final PlayerController playerController1;
final record = Record();
int _recordDuration = 0;
Timer? _timer;
RecordState _recordState = RecordState.stop;
StreamSubscription<RecordState>? _recordSub;
final List<File> _audioFile = [];
#override
void initState() {
super.initState();
//Listening record state
_recordSub = record.onStateChanged().listen((recordState) {
setState(() => _recordState = recordState);
});
initCheckPermission();
setState(() {
initFileInDir();
});
}
#override
void dispose() {
_timer?.cancel();
_recordSub?.cancel();
record.dispose();
super.dispose();
}
Future initCheckPermission() async {
final statusMicrophone = await Permission.microphone.request();
final statusStorage = await Permission.storage.request();
if (statusMicrophone != PermissionStatus.granted ||
statusStorage != PermissionStatus.granted) {
await Permission.microphone.request();
await Permission.storage.request();
}
}
void _startTimer() {
_timer?.cancel();
_timer = Timer.periodic(const Duration(seconds: 1), (Timer t) {
setState(() => _recordDuration++);
});
}
Widget _buildText() {
if (_recordState != RecordState.stop) {
return _buildTimer();
}
return const Text("Waiting to record");
}
String _formatNumber(int number) {
String numberStr = number.toString();
if (number < 10) {
numberStr = '0$numberStr';
}
return numberStr;
}
Widget _buildTimer() {
final String minutes = _formatNumber(_recordDuration ~/ 60);
final String seconds = _formatNumber(_recordDuration % 60);
return Text(
'$minutes : $seconds',
style: const TextStyle(color: Colors.red),
);
}
Future<Directory> _getDir() async {
//Check platform and adjust settings
final tempDir = Platform.isAndroid
? await getExternalStorageDirectory()
: await getApplicationDocumentsDirectory();
Directory finalPath;
if (Platform.isAndroid) {
String buildDir =
tempDir!.path.replaceFirst("/data/", "/media/").split("files").first;
buildDir += "AudioRecorder"; // Plus ajout du nom de l'application
return finalPath = await Directory(buildDir).create(recursive: true);
} else {
return finalPath = await Directory(tempDir!.path).create(recursive: true);
}
}
void initFileInDir() async {
Directory finalPath = await _getDir();
var exists = await finalPath.exists();
if (exists) {
finalPath.list(recursive: false).forEach((element) {
File file = File(element.path);
_audioFile.add(file);
});
}
}
Widget _buildStopAndPlayIcon() {
if (_recordState != RecordState.stop) {
return const Icon(Icons.stop);
} else {
return const Icon(Icons.mic);
}
}
Future<void> _start() async {
Directory finalPath = await _getDir();
//Start recording
await record.start(
path: '${finalPath.path}/${DateTime.now().millisecondsSinceEpoch}.m4a',
encoder: AudioEncoder.aacLc, // by default
bitRate: 128000, // by default
samplingRate: 44100);
_recordDuration = 0;
_startTimer();
}
Future<File?> _stop() async {
_timer?.cancel();
_recordDuration = 0;
final path = await record.stop();
File? file;
if (path != null) {
file = File(path);
if (kDebugMode) {
print("--------------------------$path");
}
}
return file;
}
Future<void> _pause() async {
_timer?.cancel();
await record.pause();
}
Future<void> _resume() async {
_startTimer();
await record.resume();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Padding(
padding: const EdgeInsets.all(10.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: [
LimitedBox(
maxHeight: 650,
child: ListView.builder(
shrinkWrap: true,
itemCount: _audioFile.length,
itemBuilder: (context, index) {
var data = _audioFile[index];
return AudioWaveFormsWidget(path: data);
},
),
),
const Spacer(),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
_buildText(),
ElevatedButton(
onPressed: () async {
if (_recordState == RecordState.stop) {
_start();
} else {
File? newFile = await _stop();
setState((){ _audioFile.add(newFile!);});
}
},
style: ButtonStyle(
elevation: MaterialStateProperty.all(0),
shape: MaterialStateProperty.all(RoundedRectangleBorder(
borderRadius: BorderRadius.circular(100),
side: BorderSide.none))),
child: _buildStopAndPlayIcon(),
)
],
),
],
),
),
// This trailing comma makes auto-formatting nicer for build methods.
);
}
}
import 'dart:io';
import 'package:audio_waveforms/audio_waveforms.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
class AudioWaveFormsWidget extends StatefulWidget {
final File path;
//final PlayerController playerController1;
const AudioWaveFormsWidget({Key? key, required this.path}) : super(key: key);
#override
State<AudioWaveFormsWidget> createState() => _AudioWaveFormsWidgetState();
}
class _AudioWaveFormsWidgetState extends State<AudioWaveFormsWidget> {
late final PlayerController playerController1;
#override
void initState() {
playerController1 = PlayerController()
..addListener(() {
if (mounted) setState(() {});
});
_preparePlayer();
super.initState();
}
void _preparePlayer() async {
bool exist = await widget.path.exists();
if (exist) {
playerController1.preparePlayer(widget.path.path);
}
print("--------------------------------${playerController1.bufferData}");
}
void _playOrPausePlayer(PlayerController controller) async {
debugPrint(
'****************************************${controller.playerState.toString()}');
controller.playerState == PlayerState.playing
? await controller.pausePlayer()
: await controller.startPlayer(finishMode: FinishMode.pause);
}
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10.0),
shape: BoxShape.rectangle,
color: Colors.lightGreenAccent,
),
child: Row(
children: [
IconButton(
onPressed: () {
_playOrPausePlayer(playerController1);
},
icon: playerController1.playerState == PlayerState.playing
? const Icon(Icons.pause)
: const Icon(Icons.play_arrow)),
AudioFileWaveforms(
density: 1.0,
enableSeekGesture: true,
size: const Size(100.0, 70.0),
playerController: playerController1),
],
)),
);
}
}
if you're trying to record, try setting the bitRate to 48000 (worked for me on iOS when it wasn't showing the waveforms when recording).
final RecorderController recorderController = RecorderController()
..androidEncoder = AndroidEncoder.aac
..androidOutputFormat = AndroidOutputFormat.mpeg4
..iosEncoder = IosEncoder.kAudioFormatMPEG4AAC
..sampleRate = 44100
..bitRate = 48000;
I'm fetching images from https://pixabay.com/api/docs/ this web-page.
I'm trying to save that image with onTap() {} with Shared Preferences package and set as background on previous page. I tried to make Shared preferences class seperate but it was showing errors. So pref.setString() method I did in ontap() {} and pref.getString() in my Center() widget with ternary operator to show that if image url is empty, write text, if it contains image url, show image. I tried to put it into setState() {} method but there are all red squiggles. How can I achieve this?
Thank you very much in advance.
Here is my code:
this is the page I'm trying to set background image:
import 'package:abcde/authentication_screen/pages/photos_page.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
class AfterAuth extends StatefulWidget {
AfterAuth({Key? key}) : super(key: key);
static const String pathId = 'Welcome Page';
#override
State<AfterAuth> createState() => _AfterAuthState();
}
class _AfterAuthState extends State<AfterAuth> {
String welcomeScreen = 'Welcome to the club';
#override
void initState() {
// TODO: implement initState
super.initState();
//final prefs = await SharedPreferences.getInstance();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Welcome'),
),
drawer: Drawer(
backgroundColor: Colors.white,
child: ListView(
padding: EdgeInsets.zero,
children: [
const DrawerHeader(
decoration: BoxDecoration(
color: Colors.lightBlueAccent,
),
child: Text('Welcome'),
),
ListTile(
title: const Text(
'Bonjour',
),
onTap: () {
setState(() {
welcomeScreen = 'Bonjour';
});
Navigator.pop(context);
},
),
ListTile(
title: const Text('Hello'),
onTap: () {
setState(() {
welcomeScreen = 'Hello';
});
Navigator.pop(context);
},
),
ListTile(
title: const Text('Guten Tag'),
onTap: () {
setState(() {
welcomeScreen = 'Guten Tag';
});
Navigator.pop(context);
},
),
ListTile(
title: const Text('Au Revoir'),
onTap: () {
setState(() {
welcomeScreen = 'Au Revoir';
});
Navigator.pop(context);
},
),
ListTile(
title: const Text('Chao Bambino'),
onTap: () {
setState(() {
welcomeScreen = 'Chao Bambino';
});
Navigator.pop(context);
},
),
],
),
),
body: Center(
child: address == ''
? Text(welcomeScreen)
: Image(
image: NetworkImage(address),
),
),
floatingActionButton: FloatingActionButton(
backgroundColor: Colors.lightBlue,
onPressed: () {
Navigator.pushNamed(context, PhotosPage.pathId);
},
child: const Icon(
Icons.add,
color: Colors.white,
),
),
);
}
String address = '';
internalMemory() async {
final prefs = await SharedPreferences.getInstance();
address = prefs.getString('urlAddress')!;
/* if(address!.isNotEmpty) {
isAddressEmpty = false;
}*/
return address;
}
}
this is the page where I fetch images from api and trying to save with Shared Preferences. actually not sure if it saves and not sure how to check it:
import 'package:flutter/material.dart';
import '../../services/photos_service.dart';
import 'package:shared_preferences/shared_preferences.dart';
class PhotosPage extends StatefulWidget {
const PhotosPage({Key? key}) : super(key: key);
static const String pathId = 'Photos page';
#override
State<PhotosPage> createState() => _PhotosPageState();
}
class _PhotosPageState extends State<PhotosPage> {
PhotosService photosService = PhotosService();
#override
void initState() {
super.initState();
photosService.getPhotos();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Photos'),
),
body: Padding(
padding: const EdgeInsets.all(16),
child: Column(children: [
_getProductTypeList(),
]),
),
);
}
Widget _buildListItem(
BuildContext context, AsyncSnapshot<dynamic> snapshot, int index) {
final photoItem = snapshot.data[index].previewURL;
print('photoItem is $photoItem');
return photoCard(context, photoItem);
}
Widget _buildList(BuildContext context, AsyncSnapshot<dynamic> snapshot) {
var values = snapshot.data;
return ListView.builder(
itemCount: snapshot.hasData ? snapshot.data.length : 0,
itemBuilder: (context, index) {
return _buildListItem(context, snapshot, index);
},
);
}
Widget _getProductTypeList() {
return Expanded(
child: FutureBuilder(
future: photosService.getPhotos(),
builder: (BuildContext context, AsyncSnapshot<dynamic> snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(
child: LinearProgressIndicator(),
);
}
return _buildList(context, snapshot);
},
),
);
}
}
Widget photoCard(BuildContext context, String url) {
return GestureDetector(
onTap: () async {
final prefs = await SharedPreferences.getInstance();
await prefs.setString('urlAddress', url);
const snackBar =
SnackBar(content: Text('You saved image'));
ScaffoldMessenger.of(context).showSnackBar(snackBar);
},
child: Card(
child: Image(
image: NetworkImage(url),
),
),
);
}
this is my service/model
import 'dart:convert';
import 'package:http/http.dart';
const apiKey = '26711456-bde74f403cb42e77029bc1678';
const appUrl = 'https://pixabay.com/api/';
class PhotosService {
Future getData(String url) async {
print('Calling url: $url');
final response = await get(Uri.parse(url));
if (response.statusCode == 200) {
return response.body;
} else {
print(response.statusCode);
}
}
String? query;
List<PhotosModel> dataList = [];
Future<List<PhotosModel>> getPhotos() async {
final photosData = await getData('$appUrl?key=$apiKey&q=$query');
var data = json.decode(photosData);
var items = data["hits"];
items.forEach((element) {
dataList.add(PhotosModel.fromJson(element));
});
print('this is photos data: $photosData');
return dataList;
}
}
class PhotosModel {
String previewURL;
PhotosModel({required this.previewURL});
factory PhotosModel.fromJson(Map<dynamic, dynamic> json) =>
_commentFromJson(json);
Map<dynamic, dynamic> toJson() => photosModelToJson(this);
}
PhotosModel _commentFromJson(Map<dynamic, dynamic> json) {
return PhotosModel(
previewURL: json['previewURL'],
);
}
Map<dynamic, dynamic> photosModelToJson(PhotosModel instance) =>
<dynamic, dynamic>{
'previewURL': instance.previewURL,
};
Call your function on initState to set the background on restart the app.
#override
void initState() {
// TODO: implement initState
super.initState();
internalMemory();
//final prefs = await SharedPreferences.getInstance();
}
add setstate to your internalMemory() function to set background
internalMemory() async {
final prefs = await SharedPreferences.getInstance();
address = await prefs.getString('urlAddress')!;
setState((){});
}
also, add the function below to set the background when you navigate back.
floatingActionButton: FloatingActionButton(
backgroundColor: Colors.lightBlue,
onPressed: () async {
await Navigator.pushNamed(context, PhotosPage.pathId);
internalMemory();
},
child: const Icon(
Icons.add,
color: Colors.white,
),
),
What I need is to "edit" a text file that is on my assets folder.
To read a file from assets I can use this:
`Future<String> setFileData(String path) async {
return await rootBundle.loadString(path);
}`
But how can I write that file from the same path?
This document can help you:
Read and write files - Flutter
Complete example
import 'dart:async';
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:path_provider/path_provider.dart';
void main() {
runApp(
MaterialApp(
title: 'Reading and Writing Files',
home: FlutterDemo(storage: CounterStorage()),
),
);
}
class CounterStorage {
Future<String> get _localPath async {
final directory = await getApplicationDocumentsDirectory();
return directory.path;
}
Future<File> get _localFile async {
final path = await _localPath;
return File('$path/counter.txt');
}
Future<int> readCounter() async {
try {
final file = await _localFile;
// Read the file
String contents = await file.readAsString();
return int.parse(contents);
} catch (e) {
// If encountering an error, return 0
return 0;
}
}
Future<File> writeCounter(int counter) async {
final file = await _localFile;
// Write the file
return file.writeAsString('$counter');
}
}
class FlutterDemo extends StatefulWidget {
final CounterStorage storage;
FlutterDemo({Key key, #required this.storage}) : super(key: key);
#override
_FlutterDemoState createState() => _FlutterDemoState();
}
class _FlutterDemoState extends State<FlutterDemo> {
int _counter;
#override
void initState() {
super.initState();
widget.storage.readCounter().then((int value) {
setState(() {
_counter = value;
});
});
}
Future<File> _incrementCounter() {
setState(() {
_counter++;
});
// Write the variable as a string to the file.
return widget.storage.writeCounter(_counter);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Reading and Writing Files')),
body: Center(
child: Text(
'Button tapped $_counter time${_counter == 1 ? '' : 's'}.',
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
I'm trying to read file from a known path get base64 string from the image. I still don't understand why base64 is broken. This happens only in flutter code.
I did try doing the same conversion using a simple dart program i got the desired base64 string. This issue is happening in flutter.
onPressed: () async {
File imageFile =
await ImagePicker.pickImage(source: ImageSource.camera);
if (imageFile != null) {
Uint8List bytes = await imageFile.readAsBytes();
String base64data = base64.encode(bytes);
print(base64data);
}
}
Following is my console output.
W/ExifInterface(15331): Skip the tag entry since tag number is not defined: 2
W/ExifInterface(15331): Stop reading file since a wrong offset may cause an infinite loop: 0
I/chatty (15331): uid=10271(com.crowdanalytix.datax_app) Binder:15331_5 identical 2 lines
W/ExifInterface(15331): Stop reading file since a wrong offset may cause an infinite loop: 0
I/flutter (15331): /9j/4QGLRXhpZgAATU0AKgAAAAgACAEAAAQAAAABAAAKMgEQAAIAAAAOAAAAbgEBAAQAAAABAAASIAEPAAIAAAAIAAAAfIdpAAQAAAABAAAAmAESAAMAAAABAAAAAAEyAAIAAAAUAAAAhIglAAQAAAABAAABCgAAAABPTkVQTFVTIEEzMDAzAE9uZVBsdXMAMjAxOTowOTowMiAxOTo0MDo1MgAAB6QDAAMAAAABAAAAAIgnAAMAAAABD6AAAJIKAAUAAAABAAAA8oKaAAUAAAABAAAA+pIJAAMAAAABABAAAJIIAAQAAAABAAAAAIKdAAUAAAABAAABAgAAAAAAAAGqAAAAZAAAAkwAACcQAABOIAAAJxAAAQAFAAEAAAAHAAABHAAAAAAyMDAvMTAwAAQBEAACAAAADgAAAVkBDwACAAAACAAAAWcBEgADAAAAAQAAAAABMgACAAAAFAAAAW8AAAAAT05FUExVUyBBMzAwMwBPbmVQbHVzADIwMTk6MDk6MDIgMTk6NDA6NTIA/+AAEEpGSUYAAQEAAAEAAQAA/9sAQwABAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEB/9sAQwEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEB/8AAEQgSIAoyAwEiAAIRAQMRAf/EAB8AAAIDAQEBAQEBAQAAAAAAAAQFAwYHAggAAQkKC//EAF0QAAEBBAYHCAIBAwMDAQECHwIBAAMREgQhIjFBUQUyYXGBkfAGE0KhscHR4VLxIwcUYjNDchVTgghjkhYkc4OiNJOjF0RUstIlZISzwsPilMTTGHSkNVW01PP0/8QAHAEAAwEBAQEBAQAAAAAAAAAAAgMEAAEFBgcI/8QAQhEAAAQDBgUFAQEAAQQBAgENAAECEQMhMRJBUWFx8IG
Base64 string which i'm getting is invalid. Can someone try this?
I'm using a OnePlus3t (ONEPLUS A3003).
print function did not print everything.
you can see full result with debug mode
code snippet
List<int> imageBytes = await _imageFile.readAsBytes();
String base64Image = base64Encode(imageBytes);
print(base64Image);
Copy base64 string from debug mode and paste to online converter, you can see result is correct.
full code from offical demo and add base64 convert
// Copyright 2019 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:async';
import 'dart:io';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:video_player/video_player.dart';
import 'dart:typed_data';
import 'package:image/image.dart' as ImageProcess;
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;
bool isVideo = false;
VideoPlayerController _controller;
String _retrieveDataError;
Future<void> _playVideo(File file) async {
if (file != null && mounted) {
await _disposeVideoController();
_controller = VideoPlayerController.file(file);
await _controller.setVolume(1.0);
await _controller.initialize();
await _controller.setLooping(true);
await _controller.play();
setState(() {});
}
}
void _onImageButtonPressed(ImageSource source) async {
if (_controller != null) {
await _controller.setVolume(0.0);
}
if (isVideo) {
final File file = await ImagePicker.pickVideo(source: source);
await _playVideo(file);
} else {
try {
_imageFile = await ImagePicker.pickImage(source: source);
List<int> imageBytes = await _imageFile.readAsBytes();
String base64Image = base64Encode(imageBytes);
print(base64Image);
setState(() {});
} catch (e) {
_pickImageError = e;
}
}
}
#override
void deactivate() {
if (_controller != null) {
_controller.setVolume(0.0);
_controller.pause();
}
super.deactivate();
}
#override
void dispose() {
_disposeVideoController();
super.dispose();
}
Future<void> _disposeVideoController() async {
if (_controller != null) {
await _controller.dispose();
_controller = null;
}
}
Widget _previewVideo() {
final Text retrieveError = _getRetrieveErrorWidget();
if (retrieveError != null) {
return retrieveError;
}
if (_controller == null) {
return const Text(
'You have not yet picked a video',
textAlign: TextAlign.center,
);
}
return Padding(
padding: const EdgeInsets.all(10.0),
child: AspectRatioVideo(_controller),
);
}
Widget _previewImage() {
final Text retrieveError = _getRetrieveErrorWidget();
if (retrieveError != null) {
return retrieveError;
}
if (_imageFile != null) {
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) {
if (response.type == RetrieveType.video) {
isVideo = true;
await _playVideo(response.file);
} else {
isVideo = false;
setState(() {
_imageFile = response.file;
});
}
} else {
_retrieveDataError = response.exception.code;
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
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 isVideo ? _previewVideo() : _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,
);
}
}
},
)
: (isVideo ? _previewVideo() : _previewImage()),
),
floatingActionButton: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
FloatingActionButton(
onPressed: () {
isVideo = false;
_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: () {
isVideo = false;
_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(
backgroundColor: Colors.red,
onPressed: () {
isVideo = true;
_onImageButtonPressed(ImageSource.gallery);
},
heroTag: 'video0',
tooltip: 'Pick Video from gallery',
child: const Icon(Icons.video_library),
),
),
Padding(
padding: const EdgeInsets.only(top: 16.0),
child: FloatingActionButton(
backgroundColor: Colors.red,
onPressed: () {
isVideo = true;
_onImageButtonPressed(ImageSource.camera);
},
heroTag: 'video1',
tooltip: 'Take a Video',
child: const Icon(Icons.videocam),
),
),
],
),
);
}
Text _getRetrieveErrorWidget() {
if (_retrieveDataError != null) {
final Text result = Text(_retrieveDataError);
_retrieveDataError = null;
return result;
}
return null;
}
}
class AspectRatioVideo extends StatefulWidget {
AspectRatioVideo(this.controller);
final VideoPlayerController controller;
#override
AspectRatioVideoState createState() => AspectRatioVideoState();
}
class AspectRatioVideoState extends State<AspectRatioVideo> {
VideoPlayerController get controller => widget.controller;
bool initialized = false;
void _onVideoControllerUpdate() {
if (!mounted) {
return;
}
if (initialized != controller.value.initialized) {
initialized = controller.value.initialized;
setState(() {});
}
}
#override
void initState() {
super.initState();
controller.addListener(_onVideoControllerUpdate);
}
#override
void dispose() {
controller.removeListener(_onVideoControllerUpdate);
super.dispose();
}
#override
Widget build(BuildContext context) {
if (initialized) {
return Center(
child: AspectRatio(
aspectRatio: controller.value?.aspectRatio,
child: VideoPlayer(controller),
),
);
} else {
return Container();
}
}
}
I'm trying to capture an image from camera and save it in cache(i.e., Directory.systemTemp available from dart.io package). It is working fine in debug mode. But when I build the release apk and install, it is not working. This is the code:
import 'dart:async';
import 'dart:io';
import 'package:camera/camera.dart';
import 'package:flutter/material.dart';
import 'package:path_provider/path_provider.dart';
class CameraExampleHome extends StatefulWidget {
#override
_CameraExampleHomeState createState() {
return new _CameraExampleHomeState();
}
}
/// Returns a suitable camera icon for [direction].
IconData getCameraLensIcon(CameraLensDirection direction) {
switch (direction) {
case CameraLensDirection.back:
return Icons.camera_rear;
case CameraLensDirection.front:
return Icons.camera_front;
case CameraLensDirection.external:
return Icons.camera;
}
throw new ArgumentError('Unknown lens direction');
}
void logError(String code, String message) =>
print('Error: $code\nError Message: $message');
class _CameraExampleHomeState extends State<CameraExampleHome> {
CameraController controller;
String imagePath;
final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
Future<List<CameraDescription>> getCameras() async {
return await availableCameras();
}
#override
Widget build(BuildContext context) {
return new Scaffold(
key: _scaffoldKey,
appBar: new AppBar(
title: const Text('Camera example'),
),
body: FutureBuilder(
future: getCameras(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
case ConnectionState.waiting:
return new Text('loading...');
default:
if (snapshot.hasError)
return new Text('Error: ${snapshot.error}');
else {
if (snapshot.hasData)
return createCameraView(context, snapshot);
else
return new Text('Null');
}
}
},
),
);
}
/// Display the preview from the camera (or a message if the preview is not available).
Widget _cameraPreviewWidget() {
if (controller == null || !controller.value.isInitialized) {
return const Text(
'Tap a camera',
style: const TextStyle(
color: Colors.white,
fontSize: 24.0,
fontWeight: FontWeight.w900,
),
);
} else {
return new AspectRatio(
aspectRatio: controller.value.aspectRatio,
child: new CameraPreview(controller),
);
}
}
/// Display the thumbnail of the captured image or video
/// Display the control bar with buttons to take pictures and record videos.
Widget _captureControlRowWidget() {
return new Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
new IconButton(
icon: const Icon(Icons.camera_alt),
color: Colors.blue,
onPressed: controller != null &&
controller.value.isInitialized &&
!controller.value.isRecordingVideo
? onTakePictureButtonPressed
: null,
),
],
);
}
/// Display a row of toggle to select the camera (or a message if no camera is available).
Widget _cameraTogglesRowWidget(var cameras) {
final List<Widget> toggles = <Widget>[];
if (cameras.isEmpty) {
return const Text('No camera found');
} else {
for (CameraDescription cameraDescription in cameras) {
toggles.add(
new SizedBox(
width: 90.0,
child: new RadioListTile<CameraDescription>(
title:
new Icon(getCameraLensIcon(cameraDescription.lensDirection)),
groupValue: controller?.description,
value: cameraDescription,
onChanged: controller != null && controller.value.isRecordingVideo
? null
: onNewCameraSelected,
),
),
);
}
}
return new Row(children: toggles);
}
String timestamp() => new DateTime.now().millisecondsSinceEpoch.toString();
void showInSnackBar(String message) {
_scaffoldKey.currentState
.showSnackBar(new SnackBar(content: new Text(message)));
}
void onNewCameraSelected(CameraDescription cameraDescription) async {
if (controller != null) {
await controller.dispose();
}
controller = new CameraController(cameraDescription, ResolutionPreset.high);
// If the controller is updated then update the UI.
controller.addListener(() {
if (mounted) setState(() {});
if (controller.value.hasError) {
showInSnackBar('Camera error ${controller.value.errorDescription}');
}
});
try {
await controller.initialize();
} on CameraException catch (e) {
_showCameraException(e);
}
if (mounted) {
setState(() {});
}
}
void onTakePictureButtonPressed() {
takePicture().then((String filePath) {
if (mounted) {
setState(() {
imagePath = filePath;
// videoController?.dispose();
// videoController = null;
});
if (filePath != null) showInSnackBar('Picture saved to $filePath');
}
});
}
Future<String> takePicture() async {
if (!controller.value.isInitialized) {
showInSnackBar('Error: select a camera first.');
return null;
}
//final Directory extDir = await getApplicationDocumentsDirectory();
final Directory extDir = Directory.systemTemp;
final String dirPath = '${extDir.path}/Pictures/flutter_test';
await new Directory(dirPath).create(recursive: true);
final String filePath = '$dirPath/${timestamp()}.jpg';
if (controller.value.isTakingPicture) {
// A capture is already pending, do nothing.
return null;
}
try {
await controller.takePicture(filePath);
} on CameraException catch (e) {
_showCameraException(e);
return null;
}
return filePath;
}
void _showCameraException(CameraException e) {
logError(e.code, e.description);
showInSnackBar('Error: ${e.code}\n${e.description}');
}
Widget createCameraView(BuildContext context, AsyncSnapshot snapshot) {
return new Column(
children: <Widget>[
new Expanded(
child: new Container(
width: MediaQuery.of(context).size.width,
child: _cameraPreviewWidget(),
),
),
_captureControlRowWidget(),
new Padding(
padding: const EdgeInsets.all(5.0),
child: new Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
_cameraTogglesRowWidget(snapshot.data),
//_thumbnailWidget(),
],
),
),
],
);
}
}
class CameraApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
home: new CameraExampleHome(),
);
}
}
List<CameraDescription> cameras;
void main() {
// Fetch the available cameras before initializing the app.
runApp(new CameraApp());
}
I've tried setting the directory to await getApplicationDocumentsDirectory() like this:
final Directory extDir = await getApplicationDocumentsDirectory();
//final Directory extDir = Directory.systemTemp;
final String dirPath = '${extDir.path}/Pictures/flutter_test';
await new Directory(dirPath).create(recursive: true);
final String filePath = '$dirPath/${timestamp()}.jpg';
Then the pictures are being saved both in debug mode and the release apk. But the pictures are lost once I relaunch the app. I want them to accessible even after the app is relaunched(that is the main reason why I want to use cache).
So what's going wrong when I try to save to cache in release build of the app?
use the path_provider package instead to get the temp path https://pub.dartlang.org/packages/path_provider
Directory tempDir = await getTemporaryDirectory();
String tempPath = tempDir.path;
Directory.systemTemp is only supposed to be used on the server/console.