I'm trying to write a code to use the camera with Flutter, but even by following the steps seen online it cannot initialize cameraController.
Here is my code :
class CameraPage extends StatefulWidget {
const CameraPage({Key? key}) : super(key: key);
#override
State<CameraPage> createState() => _CameraPageState();
}
class _CameraPageState extends State<CameraPage> {
late List<CameraDescription> cameras;
late CameraController cameraController;
#override
void initState() {
startCamera();
super.initState();
}
void startCamera() async {
cameras = await availableCameras();
cameraController = CameraController(
cameras[0],
ResolutionPreset.high
);
print(" Camera controller : $cameraController");
cameraController.initialize().then((value) {
if(!mounted) {
return;
}
setState(() {}); //To refresh widget
}).catchError((e) {
print(e);
});
}
#override
void dispose() {
cameraController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
if(cameraController.value.isInitialized) {
return Scaffold(
body: Stack(
children: [
CameraPreview(cameraController),
],
),
);
} else {
return SizedBox();
}
}
}
The print(" Camera controller : $cameraController"); is working fine and returns me a camera controller, so it might be initialized at some point ?
I will suggest using FutureBuilder to handle future method.
class _CameraPageState extends State<CameraPage> {
late List<CameraDescription> cameras;
late CameraController cameraController;
#override
void initState() {
super.initState();
}
Future<void> startCamera() async {
cameras = await availableCameras();
cameraController = CameraController(cameras[0], ResolutionPreset.high);
}
late final cameraInit = Future.wait([
startCamera(),
cameraController.initialize(),
]);
#override
void dispose() {
cameraController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: [
FutureBuilder(
future: cameraInit,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
return CameraPreview(cameraController);
} else {
return const Center(child: CircularProgressIndicator());
}
},
),
],
),
);
}
}
Also check this cookbook.
Your problem is that in the first build your camera controller is not initialized yet, because initialization is in an async method. You can use a Boolean flag to track is initialized, before accessing the late property.
bool _cameraInitialized = false;
void startCamera() async {
cameras = await availableCameras();
cameraController = CameraController(cameras[0], ResolutionPreset.high);
print(" Camera controller : $cameraController");
cameraController.initialize().then((value) {
if (!mounted) {
return;
}
setState(() {
_cameraInitialized = true; // updating the flag after camera is initialized
}); //To refresh widget
}).catchError((e) {
print(e);
});
}
#override
Widget build(BuildContext context) {
if (_cameraInitialized && cameraController.value.isInitialized) {
return Scaffold(
body: Stack(
children: [
CameraPreview(cameraController),
],
),
);
} else {
return SizedBox();
}
}
Related
The longRequest() completes with a code 200, but ui displays 400 when the longRequest() is completed. What's wrong with this scenario?
class Parsit extends StatefulWidget {
#override
_ParsitState createState() => _ParsitState();
}
class _ParsitState extends State<Parsit> {
int code = 400;
#override
void initState() {
super.initState();
setState(() {
longRequest().then((value) => code = value);
});
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Center(
child: Text('$code'),
),
);
}
Future<int> longRequest() async {
final response = await Requests.get('http://google.com');
response.raiseForStatus();
...
print(response.statusCode); // 200
return response.statusCode;
}
}
you can with a boolean value check longRequest() is finished and for UI put a loading or something :
class Parsit extends StatefulWidget {
#override
_ParsitState createState() => _ParsitState();
}
class _ParsitState extends State<Parsit> {
int code = 400;
bool longRequestIsFinish = false
#override
void initState() {
super.initState();
longRequest().then((value) {
setState(() {
code = value;
longRequestIsFinish = true;
});
});
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Center(
child: longRequestIsFinish ? Text('$code') : LoadingWidget(),
),
);
}
Future<int> longRequest() async {
final response = await Requests.get('http://google.com');
response.raiseForStatus();
...
print(response.statusCode); // 200
return response.statusCode;
}
}
using FutureBuilder widget
class Parsit extends StatefulWidget {
#override
_ParsitState createState() => _ParsitState();
}
class _ParsitState extends State<Parsit> {
Future<int> code;
#override
void initState() {
super.initState();
code = longRequest();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Center(
child: FutureBuilder<int>(
future: code,
builder: (context, snaopshot) {
if (snapshot.hasData)
return Text(snapshot.data.toString());
return CircularProgressIndicator();
),
);
}
Future<int> longRequest() async {
final response = await Requests.get('http://google.com');
response.raiseForStatus();
...
print(response.statusCode); // 200
return response.statusCode;
}
}
I'm trying to display the full camera display on this page and when I run it, it returns null. This is my first try, creating a camera page, so things might look all over the place.
StoryCamera.dart
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:camera/camera.dart';
import 'package:path/path.dart';
import 'package:path_provider/path_provider.dart';
List<CameraDescription> cameras;
//CameraController controller;
class StoryCamera extends StatefulWidget {
final String currentUser;
StoryCamera({this.currentUser});
#override
_StoryCameraState createState() => _StoryCameraState();
}
class _StoryCameraState extends State<StoryCamera> {
CameraController _controller;
Future<void> _initializeControllerFuture;
bool isCameraReady = false;
bool showCapturedPhoto = false;
var ImagePath;
get pageStatus => 1;
#override
void initState() {
super.initState();
_initializeCamera();
}
Future<void> _initializeCamera() async {
final cameras = await availableCameras();
final firstCamera = cameras.first;
_controller = CameraController(firstCamera, ResolutionPreset.high);
_initializeControllerFuture = _controller.initialize();
if (!mounted) {
return Container();
}
setState(() {
isCameraReady = true;
});
}
#override
void didChangeAppLifecycleState(AppLifecycleState state) {
if (state == AppLifecycleState.resumed) {
_controller != null
? _initializeControllerFuture = _controller.initialize()
: null; //on pause camera is disposed, so we need to call again "issue is only for android"
}
}
void onCaptureButtonPressed() async {
//on camera button press
try {
final path = join(
(await getTemporaryDirectory()).path, //Temporary path
'$pageStatus${DateTime.now()}.png',
);
ImagePath = path;
await _controller.takePicture(path); //take photo
setState(() {
showCapturedPhoto = true;
});
} catch (e) {
print(e);
}
}
#override
void dispose() {
_controller?.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
final size = MediaQuery.of(context).size;
final deviceRatio = size.width / size.height;
FutureBuilder<void>(
future: _initializeControllerFuture,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
// If the Future is complete, display the preview.
return Stack(
children: <Widget>[
Center(
child: Transform.scale(
scale: _controller.value.aspectRatio / deviceRatio,
child: new AspectRatio(
aspectRatio: _controller.value.aspectRatio,
child: new CameraPreview(_controller),
),
),
),
],
);
} else {
return Container(
child:
CircularProgressIndicator()); // Otherwise, display a loading indicator.
}
},
);
}
}
The error also points to the page before it that navigates you to this page.
StoryPage.dart
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => StoryCamera())),
Can anyone tell me why this is happening?
You need to add a return
return FutureBuilder<void>(
in my simple code as new screen, unfortunately FutureBuilder work and get data from method twice!!
i'm not sure whats problem and how can i avoid that
class LessonDetail extends StatefulWidget {
final String monthKey;
final String lessonFileKey;
LessonDetail({#required this.monthKey, #required this.lessonFileKey});
#override
State<StatefulWidget> createState() {
return _LessonDetailState(monthKey, lessonFileKey);
}
}
class _LessonDetailState extends BaseState<LessonDetail> {
String monthKey;
String lessonFileKey;
_LessonDetailState(this.monthKey, this.lessonFileKey);
#override
Widget build(BuildContext context) {
return Directionality(
textDirection: TextDirection.rtl,
child: Scaffold(
body: FutureBuilder(
future: _getLessonDetail(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
PlayLessonResponse response = snapshot.data;
print(response);
}
return Center(
child: CircularProgressIndicator(),
);
}),
),
);
}
Future<PlayLessonResponse> _getLessonDetail() async {
AudioList audioList = AudioList(
'http://www.sample.com',
'aaaaa'
);
List<AudioList> lst = [audioList,audioList,audioList];
PlayLessonResponse response = PlayLessonResponse(
2,
'',
'http://www.sample.com',
'2',
lst,
1,
'ssss'
);
print('++++++++++++++++++++');
return response;
}
}
BaseState class content:
abstract class BaseState<T extends StatefulWidget> extends State {
final Connectivity _connectivity = Connectivity();
StreamSubscription<ConnectivityResult> _connectivitySubscription;
bool isOnline = true;
Future<void> initConnectivity() async {
try {
await _connectivity.checkConnectivity();
} on PlatformException catch (e) {
print(e.toString());
}
if (!mounted) {
return;
}
await _updateConnectionStatus().then((bool isConnected){
if(mounted){
setState(() {
isOnline = isConnected;
});
}
});
}
#override
void initState() {
super.initState();
initConnectivity();
_connectivitySubscription = Connectivity()
.onConnectivityChanged
.listen((ConnectivityResult result) async {
await _updateConnectionStatus().then((bool isConnected){
if(mounted){
setState(() {
isOnline = isConnected;
});
}
});
});
}
#override
void dispose() {
_connectivitySubscription.cancel();
super.dispose();
}
Future<bool> _updateConnectionStatus() async {
bool isConnected;
try {
final List<InternetAddress> result =
await InternetAddress.lookup('google.com');
if (result.isNotEmpty && result[0].rawAddress.isNotEmpty) {
isConnected = true;
}
} on SocketException catch (_) {
isConnected = false;
return false;
}
return isConnected;
}
}
output:
I/flutter (32289): ++++++++++++++++++++
I/flutter (32289): ++++++++++++++++++++
Just like what #Ricardo said, you shouldn't call the function directly inside the FutureBuilder's future method.
Instead, you should 1st run your function in init state, and store the response in a new variable. Only then assign variable to the future of FutureBuilder.
Code Example:
class LessonDetail extends StatefulWidget {
final String monthKey;
final String lessonFileKey;
LessonDetail({#required this.monthKey, #required this.lessonFileKey});
#override
State<StatefulWidget> createState() {
return _LessonDetailState(monthKey, lessonFileKey);
}
}
class _LessonDetailState extends BaseState<LessonDetail> {
String monthKey;
String lessonFileKey;
Future<PlayLesssonResponse> _myResponse; //added this line
_LessonDetailState(this.monthKey, this.lessonFileKey);
#override
void initState() {
_myResponse = _getLessonDetail(); // added this line
super.initState();
}
#override
Widget build(BuildContext context) {
return Directionality(
textDirection: TextDirection.rtl,
child: Scaffold(
body: FutureBuilder(
future: _myResponse, //use _myResponse variable here
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
PlayLessonResponse response = snapshot.data;
print(response);
}
return Center(
child: CircularProgressIndicator(),
);
}),
),
);
}
Future<PlayLessonResponse> _getLessonDetail() async {
AudioList audioList = AudioList(
'http://www.sample.com',
'aaaaa'
);
List<AudioList> lst = [audioList,audioList,audioList];
PlayLessonResponse response = PlayLessonResponse(
2,
'',
'http://www.sample.com',
'2',
lst,
1,
'ssss'
);
print('++++++++++++++++++++');
return response;
}
}
I'm really new with flutter blocs and I having some problems with a bloc implementation, I'm trying to navigate after a state change in my splash screen widget.
After the state update to InitSuccess it should navigate to LoginScreen, but this navigation occurs many times.
I'm not able to understand what to do after the state change's to InitSuccess, after this the bloc keeps alive and calling many, many times LoginScreen.
Splash Screen
class SplashScreen extends StatefulWidget {
#override
State<StatefulWidget> createState() => _SplashScreenState();
}
class _SplashScreenState extends State<SplashScreen> {
SplashBloc _splashBloc;
final _scaffoldKey = GlobalKey<ScaffoldState>();
#override
void initState() {
_init();
super.initState();
}
#override
void dispose() {
_splashBloc.dispose();
super.dispose();
}
void _init() {
Future.delayed(Duration.zero, () {
checkDeviceConnection(context);
BlocSupervisor().delegate = SplashBlocDelegate();
final bool isIOS = Theme.of(context).platform == TargetPlatform.iOS;
_splashBloc = SplashBloc(
firebaseService: FirebaseService(context),
authService: AuthService(context),
devicesService: DevicesService(context),
);
_splashBloc.dispatch(SplashInitEvent(isIOS: isIOS));
});
#override
Widget build(BuildContext context) {
SystemChrome.setEnabledSystemUIOverlays([]);
return BlocBuilder<SplashEvent, SplashState>(
bloc: _splashBloc,
builder: (
BuildContext context,
SplashState state,
) {
if (state is InitFailure) {
Future.delayed(Duration.zero, () {
showWarningSnackBar(_scaffoldKey, state.error);
});
}
if (state is InitSuccess) {
Future.delayed(Duration.zero, () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => LoginScreen(),
),
);
});
}
return Scaffold(
key: _scaffoldKey,
body: Container(
decoration: appScreenGradient,
alignment: Alignment.center,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Image.asset(
"assets/images/splash_screen/logo_splash.png",
width: 172.88,
height: 144.55,
fit: BoxFit.contain,
),
SizedBox(
height: 20.0,
),
LoadingSpinner(
spinnerColor: Theme.of(context).primaryColorLight,
),
],
),
),
);
},
);
}
Splash Bloc
class SplashBloc extends Bloc<SplashEvent, SplashState> {
final FirebaseService firebaseService;
final DevicesService devicesService;
final AuthService authService;
final UserPreferences _userPreferences = UserPreferences();
SplashBloc({
#required this.firebaseService,
#required this.devicesService,
#required this.authService,
});
#override
Stream<SplashEvent> transform(Stream<SplashEvent> events) {
return (events as Observable<SplashEvent>).debounce(
Duration(milliseconds: 500));
}
#override
get initialState => SplashInitial();
#override
Stream<SplashState> mapEventToState(currentState, event) async* {
if (event is SplashInitEvent) {
if (currentState is SplashInitial) {
yield InitLoading();
try {
firebaseService.togglePerformanceCollection(true);
firebaseService.firebaseCloudMessagingListeners();
String firebaseToken = await firebaseService
.getFirebaseMessagingToken();
bool isRegistered =
await _userPreferences.getIsDeviceRegistered() ?? false;
if (!isRegistered) {
final String platform = event.isIOS ? 'IOS' : 'Android';
final deviceInfo = await devicesService.getDeviceInfo(platform);
isRegistered = await devicesService.register(
deviceToken: firebaseToken,
deviceInfo: deviceInfo,
);
if (isRegistered) {
_userPreferences.setIsDeviceRegistered(true);
}
}
yield InitSuccess();
} catch (e) {
yield InitFailure(error: e.toString());
}
}
}
if (event is SplashInitialEvent) {
yield SplashInitial();
}
}
}
I found the following solution:
if (state is LoggedIn) {
WidgetsBinding.instance.addPostFrameCallback((_) {
// Navigation
});
}
I wrapped my navigation with this addPostFrame callback for delaying its appearance.
I want to cycle through a list of media files (images, videos, etc..) so I have a Future that calls itself to go over the list and show each media item.
I want it to be able to play videos one after another if my list contains for example [video, video, image, video], but if I use the following way:
void playVideo(File video) {
if(playerController != null && playerController.value.initialized) {
playerController.removeListener(listener);
playerController.dispose();
}
playerController = new VideoPlayerController.file(video);
playerController.initialize().then((_) => setState(() {}));
//playerController.setVolume(0.0);
playerController.play();
playerController.addListener(listener);
}
and calling playVideo each time I have a new video to display.
If I do that, I get the following error:
A VideoPlayerController was used after being disposed.
Once you have called dispose() on a VideoPlayerController, it can no longer be used.
Below code play again button click video change and dispose
class MainScreen extends StatefulWidget {
MainScreen({Key key}) : super(key: key);
#override
_MainScreenState createState() => new _MainScreenState();
}
class _MainScreenState extends State<MainScreen> {
List<String> urlsVideo = [
"assets/videos/1.1.mp4",
"assets/videos/1.2.mp4",
];
int videoPos = 1;
VideoPlayerController controllerFirst;
StreamController<bool> streamController = new StreamController();
#override
void initState() {
_startVideoPlayer();
super.initState();
}
#override
Widget build(BuildContext context) {
return new Scaffold(
backgroundColor: Colors.white,
body: Column(
children: <Widget>[
Expanded(
child: StreamBuilder(
stream: streamController.stream,
builder: (context, snapshot) {
if (snapshot.hasData &&
!snapshot.data &&
controllerFirst != null) {
return VideoPlayer(controllerFirst);
} else {
return Center(child: CircularProgressIndicator());
}
})),
RaisedButton(
child: Padding(
padding: const EdgeInsets.all(10.0),
child: Text("Play Again"),
),
onPressed: () {
_startVideoPlayer();
},
)
],
),
);
}
Future<void> _startVideoPlayer() async {
videoPos = videoPos == 0 ? 1 : 0;
streamController.add(true);
final VideoPlayerController _controller =
VideoPlayerController.asset(urlsVideo[videoPos]);
_controller.addListener(_listener);
await _controller.setLooping(true);
await _controller.initialize();
final VideoPlayerController oldController = controllerFirst;
if (mounted) {
setState(() {
controllerFirst = _controller;
});
}
await _controller.play();
await oldController?.dispose();
streamController.add(false);
}
get _listener => () {
if (controllerFirst != null && controllerFirst.value.size != null) {
if (mounted) setState(() {});
controllerFirst.removeListener(_listener);
}
};
}