The main objective is to find an image file from the device's local storage and display it on the screen. The project uses the image_picker plugin to select images from the gallery with the permission_handler plugin. A THETA camera is not required for testing this application.
This project draws examples from the tutorials by Ivan Andrianto linked here and by Johannes Milke linked here.
To enable IOS capability, open the Flutter project with Xcode through ios/Runner.xcworkspace and ensure there is a Development Team. Also check that there is a Bundle ID for your specific project.
This project uses the permission_handler plugin. For Android, the permissions required are Permissions.storage, while for IOS, the permissions are Permissions.photos. The application switches the requested permissions depending on the platform the user runs on.
requestPermission(
Platform.isIOS ? Permission.photos : Permission.storage),Accessing a file from local storage requires permissions. To manage this, import the permission_handler plugin into the project. The package requires three steps before use in Android.
- Add these two lines to the
gradle.propertiesfile:
android.useAndroidX=true
android.enableJetifier=true- Assign the
compileSdkVersionto 33 in theandroid/app/build.gradlefile:
android {
compileSdkVersion 33}- Add the permissions to the
AndroidManifest.xmlfile in themainfolder.
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />There are two steps for use in IOS.
- Add photo permissions to the
Podfile.
## dart: PermissionGroup.photos
'PERMISSION_PHOTOS=1',- Edit the
Info.plistfile and add the below line.
<key>NSPhotoLibraryUsageDescription</key>
<string>photos</string>This project models the code example from permission_handler. Inside a class called ImageFile add the below code:
PermissionStatus _permissionStatus = PermissionStatus.denied;
@override
void initState() {
super.initState();
_listenForPermissionStatus();
}
void _listenForPermissionStatus() async {
final status;
if (Platform.isIOS) {
status = await Permission.photos.status;
} else if (Platform.isAndroid) {
status = await Permission.storage.status;
} else {
status = "error";
}
setState(() => _permissionStatus = status);
}
@override
Widget build(BuildContext context) {
...
}
Future<void> requestPermission(Permission permission) async {
final status = await permission.request();
setState(() {
print(status);
_permissionStatus = status;
print(_permissionStatus);
});
}The state of _permissionStatus is set to either storage or photos status. The Permission status will be granted, denied, restricted, or permanentlyDenied. Build a TextButton that calls the requestPermission method for Permission.storage. This call runs once to grant the app permissions.
TextButton(
child: Text("Grant Permission"),
onPressed: () => requestPermission(
Platform.isIOS ? Permission.photos : Permission.storage),
),If the _permissionStatus is equal to PermissionStatus.granted, then the Scaffold is displayed.
if (_permissionStatus == PermissionStatus.granted) {
return Scaffold(
body: Container(
...
));
})The image_picker plugin simplifies the process for selecting images. First, import the package. Create a method called pickImage that is called when a TextButton is pressed. The method updates the State when a new image is selected.
File? image;
Future pickImage() async {
final image = await ImagePicker().pickImage(
source: ImageSource.gallery,
);
if (image == null) return;
final imageTemporary = File(image.path);
print(image.path);
setState(() {
this.image = imageTemporary;
});
}If the user selects an image, then the screen displays it. Else, there is a Container.
image != null ? ImageWidget(myFile: image!) : Container()Import the panorama package into the project for 360 view. When the user clicks on the image, the Navigator.push displays the 360 view screen.
class PanoramaWidget extends StatelessWidget {
File myFile;
PanoramaWidget({Key? key, required this.myFile}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Center(
child: Panorama(
child: Image.file(myFile),
)),
);
}
}See this site for more tutorials!




