I have tried to use the Flutter camera plugin (0.2.1) in combination with a PageView and a BottomNavigationBar, but everytime the page gets switched, a few frames get skipped and the UI freezes for a second.
I've simplified my codebase for this example:
import 'package:flutter/material.dart';
import 'package:camera/camera.dart';
void main() => runApp(new Pages());
class Pages extends StatefulWidget {
@override
_PagesState createState() => _PagesState();
}
class _PagesState extends State<Pages> {
PageController _pageController;
int _page = 0;
@override
void initState() {
_pageController = new PageController();
super.initState();
}
void navTapped(int page) {
_pageController.animateToPage(page,
duration: const Duration(milliseconds: 300), curve: Curves.ease);
}
void onPageChanged(int page) {
setState(() {
this._page = page;
});
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: new Text("CameraTest"),
),
body: PageView(
children: <Widget>[Feed(), Camera(), Profile()],
controller: _pageController,
onPageChanged: onPageChanged,
),
bottomNavigationBar: new BottomNavigationBar(
items: [
new BottomNavigationBarItem(
icon: new Icon(Icons.home), title: new Text("Feed")),
new BottomNavigationBarItem(
icon: new Icon(Icons.camera), title: new Text("Capture")),
new BottomNavigationBarItem(
icon: new Icon(Icons.person), title: new Text("Profile"))
],
onTap: navTapped,
currentIndex: _page,
),
),
);
}
}
class Camera extends StatefulWidget {
@override
_CameraState createState() => _CameraState();
}
class _CameraState extends State<Camera> {
List<CameraDescription> _cameras;
CameraController _controller;
initCameras() async{
_cameras = await availableCameras();
_controller = new CameraController(_cameras[0], ResolutionPreset.medium);
await _controller.initialize();
setState(() {});
}
@override
void initState() {
initCameras();
super.initState();
}
@override
void dispose() {
_controller?.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
if (_controller == null || !_controller.value.isInitialized) {
return new Center(
child: new Text("Waiting for camera...", style: TextStyle(color: Colors.grey),),
);
}
return new AspectRatio(
aspectRatio: _controller.value.aspectRatio,
child: new CameraPreview(_controller));
}
}
//just placeholder widgets
class Feed extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(child: new Text("Feed"));
}
}
class Profile extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(child: new Text("Profile"));
}
}
There are basically three pages with the middle one showing a camera-preview (how it's supposed to look), but on switching to the camera and back from it this happens. This is really annoying since it ruins the user experience and is not smooth at all. The lag appears when calling initCameras() or when disposing the camera-controller. I tried using initCameras() in combination with a FutureBuilder, which didn't help at all, and running the method in a seperate isolate, but platform calls seem to be only allowed on the main isolate. It seems a bit weird to me since opening the camera doesn't need too much cpu power, so an async method should be fine. I am aware there is an image-picker plugin, but I want to have the preview in the app directly. I have also considered to run initCameras() on app start, but i don't want to have the camera running all the time when the user is just using another page of the app.
Is there any way to improve upon initCameras() or perhaps use a different implementation to fix the stuttering? I wouldn't even care if it takes a second to load, but i don't want any frame skips.
I followed the example on the bottom of the camera page.
Tested on physical devices as well as emulators on different Android versions.
I have solved a similar issue by adding a delay before initializing the camera:
Future.delayed(Duration(seconds: 1), () {
initCameras();
});
This way the initCameras() is called after the page navigation animation is completed. You can show a CircularProgressIndicator() to make this delay more user friendly.
I am sure there is a more neat way to work around the issue, but this seems to be the simplest solution.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With