refactoring code to use proper views in the UI and turned the camera instance into its own view with callbacks.

Also added in the model for initial use, and working to create the inferences off of TFLite using that view.
This commit is contained in:
Lucas Oskorep
2022-06-21 19:13:48 -04:00
parent 5e5a3f535f
commit 3dc77799a5
14 changed files with 1455 additions and 244 deletions
+3 -240
View File
@@ -1,22 +1,8 @@
import 'package:flutter/material.dart';
import 'package:logger/logger.dart';
import 'package:camera/camera.dart';
var logger = Logger(
printer: PrettyPrinter(),
);
late List<CameraDescription> _cameras;
import 'package:tensordex_mobile/ui/home.dart';
import 'package:tensordex_mobile/utils/logger.dart';
Future<void> main() async {
try {
WidgetsFlutterBinding.ensureInitialized();
_cameras = await availableCameras();
} on CameraException catch (e) {
logger.e('Error in fetching the cameras:', e);
}
_cameras = await availableCameras();
runApp(const MyApp());
}
@@ -26,6 +12,7 @@ class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
logger.i("Building main app");
return MaterialApp(
title: 'Tensordex',
theme: ThemeData(
@@ -44,227 +31,3 @@ class MyApp extends StatelessWidget {
);
}
}
class TensordexHome extends StatefulWidget {
const TensordexHome({Key? key, required this.title}) : super(key: key);
// This widget is the home page of your application. It is stateful, meaning
// that it has a State object (defined below) that contains fields that affect
// how it looks.
// This class is the configuration for the state. It holds the values (in this
// case the title) provided by the parent (in this case the App widget) and
// used by the build method of the State. Fields in a Widget subclass are
// always marked "final".
final String title;
@override
State<TensordexHome> createState() => _TensordexHomeState();
}
class _TensordexHomeState extends State<TensordexHome> with WidgetsBindingObserver {
int _counter = 0;
late CameraController controller;
bool _isCameraInitialized = false;
void _incrementCounter() {
setState(() {
// This call to setState tells the Flutter framework that something has
// changed in this State, which causes it to rerun the build method below
// so that the display can reflect the updated values. If we changed
// _counter without calling setState(), then the build method would not be
// called again, and so nothing would appear to happen.
_counter++;
logger.d("Counter Incremented!");
logger.w("Counter Incremented!");
logger.e("Counter Incremented!");
});
}
void onNewCameraSelected(CameraDescription cameraDescription) async {
final previousCameraController = controller;
// Instantiating the camera controller
final CameraController cameraController = CameraController(
cameraDescription,
ResolutionPreset.high,
imageFormatGroup: ImageFormatGroup.jpeg,
);
// Dispose the previous controller
await previousCameraController.dispose();
// Replace with the new controller
if (mounted) {
setState(() {
controller = cameraController;
});
}
// Update UI if controller updated
cameraController.addListener(() {
if (mounted) setState(() {});
});
// Initialize controller
try {
await cameraController.initialize();
} on CameraException catch (e) {
logger.e('Error initializing camera:', e);
}
// Update the Boolean
if (mounted) {
setState(() {
_isCameraInitialized = controller.value.isInitialized;
});
}
}
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
final CameraController cameraController = controller;
switch (state) {
case AppLifecycleState.resumed:
// --
logger.i('Resumed');
break;
case AppLifecycleState.inactive:
// --
logger.i('Inactive');
break;
case AppLifecycleState.paused:
// --
logger.i('Paused');
break;
case AppLifecycleState.detached:
// --
logger.i('Detached');
break;
}
// App state changed before we got the chance to initialize.
if (!cameraController.value.isInitialized) {
return;
}
if (state == AppLifecycleState.inactive) {
// Free up memory when camera not active
cameraController.dispose();
} else if (state == AppLifecycleState.resumed) {
// Reinitialize the camera with same properties
onNewCameraSelected(cameraController.description);
}
}
@override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
controller = CameraController(_cameras[0], ResolutionPreset.max);
controller.initialize().then((_) {
if (!mounted) {
return;
}
setState(() {onNewCameraSelected(_cameras[0]);});
}).catchError((Object e) {
if (e is CameraException) {
switch (e.code) {
case 'CameraAccessDenied':
logger.w('User denied camera access.');
controller.initialize().then((_) {
if (!mounted) {
return;
}
setState(() {});
}).catchError((Object e) {
if (e is CameraException) {
switch (e.code) {
case 'CameraAccessDenied':
logger.i('User denied camera access.');
break;
default:
logger.i('Handle other errors.');
break;
}
}
});
break;
default:
logger.i('Handle other errors.');
break;
}
}
});
}
@override
void dispose() {
controller.dispose();
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
@override
Widget build(BuildContext context) {
// This method is rerun every time setState is called, for instance as done
// by the _incrementCounter method above.
//
// The Flutter framework has been optimized to make rerunning build methods
// fast, so that you can just rebuild anything that needs updating rather
// than having to individually change instances of widgets.
return Scaffold(
appBar: AppBar(
// Here we take the value from the MyHomePage object that was created by
// the App.build method, and use it to set our appbar title.
title: Text(widget.title),
),
body: Center(
// Center is a layout widget. It takes a single child and positions it
// in the middle of the parent.
child: Column(
// Column is also a layout widget. It takes a list of children and
// arranges them vertically. By default, it sizes itself to fit its
// children horizontally, and tries to be as tall as its parent.
//
// Invoke "debug painting" (press "p" in the console, choose the
// "Toggle Debug Paint" action from the Flutter Inspector in Android
// Studio, or the "Toggle Debug Paint" command in Visual Studio Code)
// to see the wireframe for each widget.
//
// Column has various properties to control how it sizes itself and
// how it positions its children. Here we use mainAxisAlignment to
// center the children vertically; the main axis here is the vertical
// axis because Columns are vertical (the cross axis would be
// horizontal).
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
controller.value.isInitialized
? CameraPreview(controller)
: const Text("Please enable the camera!"),
],
),
),
floatingActionButton: GestureDetector(
onLongPress: () {
_incrementCounter();
},
child: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: const Icon(Icons.photo_camera),
), // This trailing comma makes auto-formatting nicer for build methods.
));
}
}