I am using this code that I got directly from pub.dev regarding initializing the camera and creating a list of available cameras
the list is created in a Future main() function but it is not being automatically called when I navigate to the CameraApp page. Has anyone run into this issue? How do I initialize the camera and create the list of available cameras when it navigates to the page with this code? Please help, thank you.
/// CameraApp is the Main Application.
class CameraApp extends StatelessWidget {
/// Default Constructor
const CameraApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return const MaterialApp(
home: CameraExampleHome(),
);
}
}
List<CameraDescription> _cameras = <CameraDescription>[];
Future<void> main() async {
// Fetch the available cameras before initializing the app.
try {
WidgetsFlutterBinding.ensureInitialized();
_cameras = await availableCameras();
} on CameraException catch (e) {
_logError(e.code, e.description);
}
runApp(const CameraApp());
}
And this is the code where I call the CameraApp function from inside a button:
ElevatedButton(
onPressed: ()
{Navigator.push(
context,
MaterialPageRoute(
builder: (context) => CameraApp()));},
child: const Text('Camera'),
style: ElevatedButton.styleFrom(
minimumSize: const Size(160.0, 35.0)),
),
You can create a kind of singleton to manage camera operations.
class CameraManager {
// Declare your camera list here
List<CameraDescription> _cameras = <CameraDescription>[];
// Constructor
CameraManager._privateConstructor() {}
// initialise instance
static final CameraManager instance =
CameraManager._privateConstructor();
// Add a getter to access camera list
List<CameraDescription> get cameras => _cameras;
// Init method
init() async {
try {
_cameras = await availableCameras();
} on CameraException catch (e) {
_logError(e.code, e.description);
}
}
// other needed methods to manage camera
...
}
And then in you main function
Future<void> main() async {
// Fetch the available cameras before initializing the app.
try {
WidgetsFlutterBinding.ensureInitialized();
await CameraManager.instance.init();
}
runApp(const CameraApp());
}
Then on other part of your application, you can import the singleton and access methods and properties with CameraManager.instance.*, for example CameraManager.instance.cameras access _cameras through the getter.
There are few things to consider here.. The implementation you did was right but you named the cameras as a private variable which will be accessed in a single dart file by adding an _ like _cameras. Removing that will make it globally available in all classes just by importing main.dart
Here is the full code
main.dart
import 'package:camera/camera.dart';
import 'package:flutter/material.dart';
import 'package:trial/CameraExampleHome.dart';
List<CameraDescription> cameras = <CameraDescription>[];
void main() async {
try {
WidgetsFlutterBinding.ensureInitialized();
cameras = await availableCameras();
print(cameras);
} on CameraException catch (e) {
print(e.toString());
}
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(home: CameraApp());
}
}
class CameraApp extends StatelessWidget {
/// Default Constructor
const CameraApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return const MaterialApp(
home: CameraExampleHome(),
);
}
}
cameraExampleHome.dart
import 'package:flutter/material.dart';
import 'main.dart';
class CameraExampleHome extends StatefulWidget {
const CameraExampleHome({Key? key}) : super(key: key);
#override
State<CameraExampleHome> createState() => _CameraExampleHomeState();
}
class _CameraExampleHomeState extends State<CameraExampleHome> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Text("Available Cameras $cameras"),
//output: Available Cameras [CameraDescription(0, CameraLensDirection.back, 90), CameraDescription(1, CameraLensDirection.front, 270), CameraDescription(2, CameraLensDirection.front, 270)]
),
);
}
}
You could create a library file for Global Variables.
Create a file called "globals.dart" in your lib folder.
Declare the following line at the top.
library your_project_name.globals;
Then set your variable in it
List<CameraDescription> cameras = <CameraDescription>[];
Usage in main
import 'globals.dart' as globals;
main() {
...
globals.cameras = await availableCameras();
...
}
Then simply use the variable anywhere in your project.
import 'globals.dart' as globals;
globals.cameras...
Declare list of CameraDescription global instance as below:
List<CameraDescription> cameras = <CameraDescription>[];
Now you can access the camera instance by importing main.dart , _ always make the instance variable private which will not be accessible outside the file
Related
I am needing to encrypt a hive box, in which the box 'user_api' is to be called in Api_Page_() to receive user input to store inside said box. However, the encryptedBox is not defined within the class. The Hive Docs display the encryption code is to be done inside of the main() function, which I have done, but I am unsure of how to take the box outside of main().
Any help or advice is greatly appreciated!
My Code:
import 'dart:convert';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:hive/hive.dart';
import 'package:path_provider/path_provider.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
// HIVE ENCRYPTION----------------------------------------
const secureStorage = FlutterSecureStorage();
final encryptionKey = await secureStorage.read(key: 'key');
if (encryptionKey == null) {
final key = Hive.generateSecureKey();
await secureStorage.write(
key: 'key',
value: base64Encode(key),
);
}
final key = await secureStorage.read(key: 'key');
final encryptKey = base64Url.decode(key);
print('Encryption key: $encryptKey');
// HIVE ENCRYPTION----------------------------------------
// HIVE INIT---------------------------------------------
Directory directory = await getApplicationDocumentsDirectory();
Hive.init(directory.path);
await Hive.initFlutter();
final encryptedBox = Hive.openBox<String>('user_api', encryptionCipher: HiveAesCipher(encryptKey)); // Initially Opens Box on App Start
// HIVE INIT---------------------------------------------
runApp(myApp());
}
class myApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return const MaterialApp(
debugShowCheckedModeBanner: false, // Removes Debug Banner. [Delete before app release]
title: 'App Title Placeholder',
home: API_Page_() // Calls API_Page_ class from api_page.dart
);
}
}
class API_Page_ extends StatelessWidget {
const API_Page_({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
body: RaisedButton(onPressed: () { var eBox = encryptedBox<String>('user_api');
},
)
);
}
}
All of the sudden I am getting the error DataStore plugin has not been added to Amplify, recoverySuggestion: Add DataStore plugin to Amplify and call configure before calling DataStore related APIs to rule out any of the work I was doing on that page I tried it on a fresh page with the same result.
I already did execute amplify codegen models, amplify pull and amplify env pull. Also tried to do a flutter clean but I don't see any change at all. I'm really puzzled and can't seem to figure out the issue.
One thing I did notice while debugging was that the initState of the screen seems to be executed earlier as the configureAmplify callback.
I will show the relevant parts of the code (sorry for the long code in advance).
Pubspec.yaml
dependencies:
...
amplify_flutter: ^0.2.10
amplify_datastore: ^0.2.10
amplify_api: ^0.2.10
amplify_auth_cognito: ^0.2.10
amplify_storage_s3: ^0.2.10
main.dart
import 'package:flutter/material.dart';
import 'package:my_package/screens/main/teams_screen.dart';
import 'package:my_package/services/amplify_services.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
#override
void initState() {
super.initState();
AmplifyService.configureAmplify();
}
#override
Widget build(BuildContext context) {
...
}
}
services/amplify_services.dart
import 'package:flutter/foundation.dart';
import 'package:amplify_flutter/amplify.dart';
import 'package:amplify_datastore/amplify_datastore.dart';
import 'package:amplify_api/amplify_api.dart';
import 'package:amplify_auth_cognito/amplify_auth_cognito.dart';
import 'package:amplify_storage_s3/amplify_storage_s3.dart';
import 'package:my_package/models/ModelProvider.dart';
import 'package:my_package/amplifyconfiguration.dart';
class AmplifyService {
static configureAmplify() async {
AmplifyAPI apiPlugin = AmplifyAPI();
AmplifyAuthCognito authPlugin = AmplifyAuthCognito();
AmplifyStorageS3 amplifyStorageS3 = AmplifyStorageS3();
AmplifyDataStore dataStorePlugin = AmplifyDataStore(
modelProvider: ModelProvider.instance,
);
await Amplify.addPlugins([
dataStorePlugin,
authPlugin,
amplifyStorageS3,
apiPlugin,
]);
try {
await Amplify.configure(amplifyconfig);
} on AmplifyAlreadyConfiguredException {
if (kDebugMode) {
print(
"Amplify was already configured. Looks like app restarted on android.");
}
}
}
}
Lastly the very basic page with not even an output (screens/teams_screen.dart)
import 'dart:async';
import 'package:amplify_datastore/amplify_datastore.dart';
import 'package:amplify_flutter/amplify.dart';
import 'package:flutter/material.dart';
import 'package:my_package/models/Team.dart';
class TeamsScreen extends StatefulWidget {
const TeamsScreen({Key? key}) : super(key: key);
#override
_TeamsScreenState createState() => _TeamsScreenState();
}
class _TeamsScreenState extends State<TeamsScreen> {
late StreamSubscription<QuerySnapshot<Team>> _teamsSubscription;
bool _isLoading = true;
List<Team> teams = [];
#override
void initState() {
super.initState();
_initializeApp();
}
#override
void dispose() {
_teamsSubscription.cancel();
super.dispose();
}
Future<void> _initializeApp() async {
_teamsSubscription = Amplify.DataStore.observeQuery(Team.classType)
.listen((QuerySnapshot<Team> snapshot) {
setState(() {
if (_isLoading) _isLoading = false;
teams = snapshot.items;
});
});
}
#override
Widget build(BuildContext context) {
return Container();
}
}
New day, fresh mind. The issue turned out to be quite simple, I didn't set an _isLoading state to indicate weter or not the configureAmplify callback was completed and let the app just continue loading all the other screens triggering the error. So after setting the state and only adding the rest of the app after the state was changed it worked without any problem.
To fix it I did the following:
import 'package:flutter/material.dart';
import 'package:my_package/screens/main/teams_screen.dart';
import 'package:my_package/services/amplify_services.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
#override
void initState() {
super.initState();
_initializeApp();
}
Future<void> _initializeApp() async {
await AmplifyService.configureAmplify(); // note the await!
setState(() {
_isLoading = false; // important to set the state!
});
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: _isLoading
? Center(child: CircularProgressIndicator())
: const MainScreen(), // _isLoading is very important here.
);
}
}
I have a simple client code in which I'm trying to pass the WebSocketChannel instance to an inner stateful widget, and for some reason when I try to run the code the app crushes and displays on the screen "Unexpected null value. See also: https://flutter.dev/docs/testing/errors". It would be greatly appreciated if someone could explain to me why this happens and how to fix it.
The code:
import 'package:flutter/material.dart';
import 'package:web_socket_channel/web_socket_channel.dart';
class TestWidget extends StatefulWidget {
final WebSocketChannel channel;
const TestWidget(this.channel);
#override
_TestWidgetState createState() => _TestWidgetState();
}
class _TestWidgetState extends State<TestWidget> {
String buttonText = '';
_TestWidgetState() {
widget.channel.stream.listen((data){
setState(() {buttonText = data;});
});
}
#override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: (){widget.channel.sink.add('hello');},
child: Text(buttonText)
);
}
}
class App extends StatelessWidget {
final WebSocketChannel channel = WebSocketChannel.connect(
Uri.parse('ws://localhost:8000/')
);
#override
Widget build(BuildContext context) {
return MaterialApp(home: Scaffold(body:
TestWidget(channel)
));
}
}
void main() {
runApp(App());
}
Thanks in advance for the help.
Any particular reason why you put
final WebSocketChannel channel = WebSocketChannel.connect(
Uri.parse('ws://localhost:8000/')
);
in App? Move this line code to TestStateWidget constructor. It's best practice u follow null safety method when try to access an object.
Background - want to utilise a dynamic list of items for a StatefulWidget. In my usecase the widget will be calling a CustomePainter (canvas) so sometimes there will be a varying number of images to be drawn on the canvas, hence within parent StatefulWidget would like to have an "array of images".
Question - if using an array as the state variable what do I need to do programmtically (if anything) to ensure only the items that have changed within the array do infact get "redrawn", in particular in this case get "re-painted" on the canvas.
(Perhaps there are two separate answers here one for the array having (a) standard widgets, and one for the case where (b) items are being passed to a CustomePainter for painting on a canvas??)
As an example see code below (#ch271828n provided this to assist me here on a separate question - Getting 'Future<Image>' can't be assigned to a variable of type 'Image' in this Flutter/Dart code?). This code highlights the main idea, but doesn't include the passing onto a CustomPainter as parameters.
import 'dart:typed_data';
import 'dart:ui' as ui;
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key}) : super(key: key);
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
List<ui.Image> _backgroundImages;
#override
void initState() {
super.initState();
_asyncInit();
}
Future<void> _asyncInit() async {
final imageNames = ['firstimage', 'secondimage', 'thirdimage'];
// NOTE by doing this, your images are loaded **simutanously** instead of **waiting for one to finish before starting the next**
final futures = [for (final name in imageNames) loadImage(name)];
final images = await Future.wait(futures);
setState(() {
_backgroundImages = images;
});
}
Future<ui.Image> loadImage(imageString) async {
ByteData bd = await rootBundle.load(imageString);
// ByteData bd = await rootBundle.load("graphics/bar-1920×1080.jpg");
final Uint8List bytes = Uint8List.view(bd.buffer);
final ui.Codec codec = await ui.instantiateImageCodec(bytes);
final ui.Image image = (await codec.getNextFrame()).image;
return image;
// setState(() => imageStateVarible = image);
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: _backgroundImages != null ? YourWidget(_backgroundImages) : Text('you are loading that image'),
),
);
}
}
Firstly, talking about builds: Indeed you need a state management solution. Maybe look at https://flutter.dev/docs/development/data-and-backend/state-mgmt/options. Personally I suggest MobX which requires few boilerplate and can make development much faster.
Using setState is not a good idea. The setState, when looking into source code, does nothing but:
void setState(VoidCallback fn) {
assert(...);
_element.markNeedsBuild();
}
So it is nothing but markNeedsBuild - the whole stateful widget is called build again. Your callback passed into setState has nothing special.
Thus this way cannot trigger partial rebuild.
Secondly, you only want to reduce repaint, but not reduce number of builds, because flutter is designed such that build can be called by 60fps easily. Then things become easy:
class MyHomePage extends StatefulWidget {
MyHomePage({Key key}) : super(key: key);
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Stack(
children: [
for (final image in _yourImages)
CustomPaint(
painter: _MyPainter(image),
),
],
);
}
}
class _MyPainter extends CustomPainter {
final ui.Image image;
_MyPainter(this.image);
#override
void paint(ui.Canvas canvas, ui.Size size) {
// paint it
}
#override
bool shouldRepaint(covariant _MyPainter oldDelegate) {
return oldDelegate.image != this.image;
}
}
Notice that, you can call setState in homepage whenever you like, because that is cheap. (if your homepage has a lot of children widget then maybe not good, then you need state management solution). But the painter will not repaint unless shouldRepaint says so. Yeah!
Im doing a splashscreen first app, to be followed by a walkthrough page if the user first used the app, else go to a welcome page to sign in/ sign up if already saw the walkthrough screen.
My code came from this projects main.dart file: https://github.com/instaflutter/flutter-login-screen-firebase-auth-facebook-login and modified it to this code(from splashscreen tutorial FlutterKart)
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:binder/ui/screens/root_screen.dart';
import 'package:binder/ui/screens/walk_screen.dart';
void main() {
Firestore.instance.settings(timestampsInSnapshotsEnabled: true);
SharedPreferences.getInstance().then((prefs) {
SplashScreen(prefs: prefs);
});
}
class SplashScreen extends StatefulWidget {
final SharedPreferences prefs;
SplashScreen({Key key,this.prefs}): super(key: key);
#override
_SplashScreenState createState() => _SplashScreenState();
}
class _SplashScreenState extends State<SplashScreen> {
#override
void initState() {
super.initState();
Timer(Duration(seconds: 3), () => _handleCurrentScreen(context));
}
#override
Widget build(BuildContext context) {
final logowhite = Hero(
tag: 'hero',
child: //code insert flutterkart splashscreen
)
],
),
)
],
)
],
),
);
}
Widget _handleCurrentScreen (BuildContext context) {
bool seen = (widget.prefs.getBool('seen') ?? false);
if (seen) {
return new RootScreen();
} else {
return new WalkthroughScreen(prefs: widget.prefs);
}
}
}
I want it to show the splashscreen first and directed to the rootscreen if already seen and to the walkthrough screen if first use.
You'd probably want to use shared_preferences or something similar. Something like this:
// add this static variable somewhere
// could technically be initialized during splash screen and added to a Provider or something similar after
static SharedPreferences prefs;
// make `main` async if it is not already
Future<void> main() async {
prefs = await SharedPreferences.getInstance();
...
}
Future<void> onSplashScreenDone() async {
if (prefs.getBool('isFirstTime') ?? true) {
// you might want to put this at the end of your walkthrough, so they don't miss it if they close the app, for example
await prefs.setBool('isFirstTime', false);
// this is their first time, show walkthrough, etc.
...
} else {
// this is not their first time, do normal things.
}
}