From e96a0d6f5e8eb2a72107a402e76b1c6e1de97dbb Mon Sep 17 00:00:00 2001 From: Julian Steenbakker Date: Wed, 23 Feb 2022 22:29:00 +0100 Subject: [PATCH] bug: fix disposing controller --- example/lib/barcode_scanner_controller.dart | 114 ++++++++ .../barcode_scanner_without_controller.dart | 70 +++++ example/lib/main.dart | 258 +++--------------- lib/src/mobile_scanner.dart | 25 +- 4 files changed, 228 insertions(+), 239 deletions(-) create mode 100644 example/lib/barcode_scanner_controller.dart create mode 100644 example/lib/barcode_scanner_without_controller.dart diff --git a/example/lib/barcode_scanner_controller.dart b/example/lib/barcode_scanner_controller.dart new file mode 100644 index 000000000..5545bb09c --- /dev/null +++ b/example/lib/barcode_scanner_controller.dart @@ -0,0 +1,114 @@ +import 'package:flutter/material.dart'; +import 'package:mobile_scanner/mobile_scanner.dart'; + +class BarcodeScannerWithController extends StatefulWidget { + const BarcodeScannerWithController({Key? key}) : super(key: key); + + @override + _BarcodeScannerWithControllerState createState() => + _BarcodeScannerWithControllerState(); +} + +class _BarcodeScannerWithControllerState + extends State + with SingleTickerProviderStateMixin { + String? barcode; + + MobileScannerController controller = MobileScannerController( + torchEnabled: true, + // facing: CameraFacing.front, + ); + + @override + Widget build(BuildContext context) { + return MaterialApp( + home: Scaffold( + backgroundColor: Colors.black, + body: Builder(builder: (context) { + return Stack( + children: [ + MobileScanner( + controller: controller, + fit: BoxFit.contain, + // controller: MobileScannerController( + // torchEnabled: true, + // facing: CameraFacing.front, + // ), + onDetect: (barcode, args) { + if (this.barcode != barcode.rawValue) { + setState(() { + this.barcode = barcode.rawValue; + }); + } + }), + Align( + alignment: Alignment.bottomCenter, + child: Container( + alignment: Alignment.bottomCenter, + height: 100, + color: Colors.black.withOpacity(0.4), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + IconButton( + color: Colors.white, + icon: ValueListenableBuilder( + valueListenable: controller.torchState, + builder: (context, state, child) { + switch (state as TorchState) { + case TorchState.off: + return const Icon(Icons.flash_off, + color: Colors.grey); + case TorchState.on: + return const Icon(Icons.flash_on, + color: Colors.yellow); + } + }, + ), + iconSize: 32.0, + onPressed: () => controller.toggleTorch(), + ), + Center( + child: SizedBox( + width: MediaQuery.of(context).size.width - 120, + height: 50, + child: FittedBox( + child: Text( + barcode ?? 'Scan something!', + overflow: TextOverflow.fade, + style: Theme.of(context) + .textTheme + .headline4! + .copyWith(color: Colors.white), + ), + ), + ), + ), + IconButton( + color: Colors.white, + icon: ValueListenableBuilder( + valueListenable: controller.cameraFacingState, + builder: (context, state, child) { + switch (state as CameraFacing) { + case CameraFacing.front: + return const Icon(Icons.camera_front); + case CameraFacing.back: + return const Icon(Icons.camera_rear); + } + }, + ), + iconSize: 32.0, + onPressed: () => controller.switchCamera(), + ), + ], + ), + ), + ), + ], + ); + }), + ), + ); + } +} diff --git a/example/lib/barcode_scanner_without_controller.dart b/example/lib/barcode_scanner_without_controller.dart new file mode 100644 index 000000000..fd3763a19 --- /dev/null +++ b/example/lib/barcode_scanner_without_controller.dart @@ -0,0 +1,70 @@ +import 'package:flutter/material.dart'; +import 'package:mobile_scanner/mobile_scanner.dart'; + +class BarcodeScannerWithoutController extends StatefulWidget { + const BarcodeScannerWithoutController({Key? key}) : super(key: key); + + @override + _BarcodeScannerWithoutControllerState createState() => + _BarcodeScannerWithoutControllerState(); +} + +class _BarcodeScannerWithoutControllerState + extends State + with SingleTickerProviderStateMixin { + String? barcode; + + @override + Widget build(BuildContext context) { + return MaterialApp( + home: Scaffold( + backgroundColor: Colors.black, + body: Builder(builder: (context) { + return Stack( + children: [ + MobileScanner( + fit: BoxFit.contain, + onDetect: (barcode, args) { + if (this.barcode != barcode.rawValue) { + setState(() { + this.barcode = barcode.rawValue; + }); + } + }), + Align( + alignment: Alignment.bottomCenter, + child: Container( + alignment: Alignment.bottomCenter, + height: 100, + color: Colors.black.withOpacity(0.4), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Center( + child: SizedBox( + width: MediaQuery.of(context).size.width - 120, + height: 50, + child: FittedBox( + child: Text( + barcode ?? 'Scan something!', + overflow: TextOverflow.fade, + style: Theme.of(context) + .textTheme + .headline4! + .copyWith(color: Colors.white), + ), + ), + ), + ), + ], + ), + ), + ), + ], + ); + }), + ), + ); + } +} diff --git a/example/lib/main.dart b/example/lib/main.dart index 5406965d7..50494c5f6 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -1,236 +1,42 @@ import 'package:flutter/material.dart'; -import 'package:mobile_scanner/mobile_scanner.dart'; +import 'package:mobile_scanner_example/barcode_scanner_controller.dart'; +import 'package:mobile_scanner_example/barcode_scanner_without_controller.dart'; -void main() { - runApp(const AnalyzeView()); -} +void main() => runApp(const MaterialApp(home: MyHome())); -class AnalyzeView extends StatefulWidget { - const AnalyzeView({Key? key}) : super(key: key); - - @override - _AnalyzeViewState createState() => _AnalyzeViewState(); -} - -class _AnalyzeViewState extends State - with SingleTickerProviderStateMixin { - String? barcode; - - MobileScannerController controller = MobileScannerController( - torchEnabled: true, - facing: CameraFacing.front, - ); +class MyHome extends StatelessWidget { + const MyHome({Key? key}) : super(key: key); @override Widget build(BuildContext context) { - return MaterialApp( - home: Scaffold( - backgroundColor: Colors.black, - body: Builder(builder: (context) { - return Stack( - children: [ - MobileScanner( - controller: controller, - fit: BoxFit.contain, - // controller: MobileScannerController( - // torchEnabled: true, - // facing: CameraFacing.front, - // ), - onDetect: (barcode, args) { - if (this.barcode != barcode.rawValue) { - setState(() { - this.barcode = barcode.rawValue; - }); - } - }), - Align( - alignment: Alignment.bottomCenter, - child: Container( - alignment: Alignment.bottomCenter, - height: 100, - color: Colors.black.withOpacity(0.4), - child: Row( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - IconButton( - color: Colors.white, - icon: ValueListenableBuilder( - valueListenable: controller.torchState, - builder: (context, state, child) { - switch (state as TorchState) { - case TorchState.off: - return const Icon(Icons.flash_off, - color: Colors.grey); - case TorchState.on: - return const Icon(Icons.flash_on, - color: Colors.yellow); - } - }, - ), - iconSize: 32.0, - onPressed: () => controller.toggleTorch(), - ), - Center( - child: SizedBox( - width: MediaQuery.of(context).size.width - 120, - height: 50, - child: FittedBox( - child: Text( - barcode ?? 'Scan something!', - overflow: TextOverflow.fade, - style: Theme.of(context) - .textTheme - .headline4! - .copyWith(color: Colors.white), - ), - ), - ), - ), - IconButton( - color: Colors.white, - icon: ValueListenableBuilder( - valueListenable: controller.cameraFacingState, - builder: (context, state, child) { - switch (state as CameraFacing) { - case CameraFacing.front: - return const Icon(Icons.camera_front); - case CameraFacing.back: - return const Icon(Icons.camera_rear); - } - }, - ), - iconSize: 32.0, - onPressed: () => controller.switchCamera(), - ), - ], - ), - ), - ), - - // Container( - // alignment: Alignment.bottomCenter, - // margin: EdgeInsets.only(bottom: 80.0), - // child: IconButton( - // icon: ValueListenableBuilder( - // valueListenable: cameraController.torchState, - // builder: (context, state, child) { - // final color = - // state == TorchState.off ? Colors.grey : Colors.white; - // return Icon(Icons.bolt, color: color); - // }, - // ), - // iconSize: 32.0, - // onPressed: () => cameraController.torch(), - // ), - // ), - ], - ); - }), + return Scaffold( + appBar: AppBar(title: const Text('Flutter Demo Home Page')), + body: SizedBox( + width: MediaQuery.of(context).size.width, + height: MediaQuery.of(context).size.height, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + ElevatedButton( + onPressed: () { + Navigator.of(context).push(MaterialPageRoute( + builder: (context) => const BarcodeScannerWithController(), + )); + }, + child: const Text('MobileScanner with Controller'), + ), + ElevatedButton( + onPressed: () { + Navigator.of(context).push(MaterialPageRoute( + builder: (context) => const BarcodeScannerWithoutController(), + )); + }, + child: const Text('MobileScanner without Controller'), + ), + ], + ), ), ); } - - @override - void dispose() { - // cameraController.dispose(); - super.dispose(); - } - - void display(Barcode barcode) { - Navigator.of(context).popAndPushNamed('display', arguments: barcode); - } } - -// import 'package:flutter/material.dart'; -// import 'package:flutter/rendering.dart'; -// import 'package:mobile_scanner/mobile_scanner.dart'; -// -// void main() { -// debugPaintSizeEnabled = false; -// runApp(HomePage()); -// } -// -// class HomePage extends StatefulWidget { -// @override -// HomeState createState() => HomeState(); -// } -// -// class HomeState extends State { -// @override -// Widget build(BuildContext context) { -// return MaterialApp(home: MyApp()); -// } -// } -// -// class MyApp extends StatefulWidget { -// @override -// _MyAppState createState() => _MyAppState(); -// } -// -// class _MyAppState extends State { -// String? qr; -// bool camState = false; -// -// @override -// initState() { -// super.initState(); -// } -// -// @override -// Widget build(BuildContext context) { -// return Scaffold( -// appBar: AppBar( -// title: Text('Plugin example app'), -// ), -// body: Center( -// child: Column( -// crossAxisAlignment: CrossAxisAlignment.center, -// mainAxisAlignment: MainAxisAlignment.center, -// children: [ -// Expanded( -// child: camState -// ? Center( -// child: SizedBox( -// width: 300.0, -// height: 600.0, -// child: MobileScanner( -// onError: (context, error) => Text( -// error.toString(), -// style: TextStyle(color: Colors.red), -// ), -// qrCodeCallback: (code) { -// setState(() { -// qr = code; -// }); -// }, -// child: Container( -// decoration: BoxDecoration( -// color: Colors.transparent, -// border: Border.all( -// color: Colors.orange, -// width: 10.0, -// style: BorderStyle.solid), -// ), -// ), -// ), -// ), -// ) -// : Center(child: Text("Camera inactive"))), -// Text("QRCODE: $qr"), -// ], -// ), -// ), -// floatingActionButton: FloatingActionButton( -// child: Text( -// "press me", -// textAlign: TextAlign.center, -// ), -// onPressed: () { -// setState(() { -// camState = !camState; -// }); -// }), -// ); -// } -// } diff --git a/lib/src/mobile_scanner.dart b/lib/src/mobile_scanner.dart index b7ed13fb2..4e4adb023 100644 --- a/lib/src/mobile_scanner.dart +++ b/lib/src/mobile_scanner.dart @@ -29,8 +29,7 @@ class MobileScanner extends StatefulWidget { this.onDetect, this.controller, this.fit = BoxFit.cover, - }) : assert((controller != null)), - super(key: key); + }) : super(key: key); @override State createState() => _MobileScannerState(); @@ -38,33 +37,32 @@ class MobileScanner extends StatefulWidget { class _MobileScannerState extends State with WidgetsBindingObserver { - bool onScreen = true; late MobileScannerController controller; @override void initState() { super.initState(); + WidgetsBinding.instance?.addObserver(this); controller = widget.controller ?? MobileScannerController(); } @override void didChangeAppLifecycleState(AppLifecycleState state) { - if (state == AppLifecycleState.resumed) { - setState(() => onScreen = true); - } else { - if (onScreen) { + switch (state) { + case AppLifecycleState.resumed: + controller.start(); + break; + case AppLifecycleState.inactive: + case AppLifecycleState.paused: + case AppLifecycleState.detached: controller.stop(); - } - setState(() { - onScreen = false; - }); + break; } } @override Widget build(BuildContext context) { return LayoutBuilder(builder: (context, BoxConstraints constraints) { - if (!onScreen) return const Text("Camera Paused."); return ValueListenableBuilder( valueListenable: controller.args, builder: (context, value, child) { @@ -112,7 +110,8 @@ class _MobileScannerState extends State @override void dispose() { - if (widget.controller == null) controller.dispose(); + controller.dispose(); + WidgetsBinding.instance?.removeObserver(this); super.dispose(); } }