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.
);
}
}
Related
I'm new to flutter and I want to implement the useEffect hook.
Here is my widget:
import 'dart:developer';
import 'package:flutter/material.dart';
class MarketRunnerChart extends StatefulWidget {
const MarketRunnerChart({Key? key}) : super(key: key);
#override
State<MarketRunnerChart> createState() => _MarketRunnerChartState();
}
class _MarketRunnerChartState extends State<MarketRunnerChart> {
#override
Widget build(BuildContext context) {
useEffect(() {
log('okok');
}, []);
return Text("Some text");
}
}
But I got the error The method 'useEffect' isn't defined for the type '_MarketRunnerChartState'.
When I remove the useEffect hook out of the build function and put it directly in the class I got error 'useEffect' must have a method body because '_MarketRunnerChartState' isn't abstract.
I'm used to work with React, but right now with flutter I can't figure out how to implement that hook.
How am I supposed to do this ?
try add
import 'package:flutter_hooks/flutter_hooks.dart';
on top of your class file
import flutter hooks
import 'package:flutter_hooks/flutter_hooks.dart';
class MarketRunnerChart extends StatefulWidget {
const MarketRunnerChart({Key? key}) : super(key: key);
#override
State<MarketRunnerChart> createState() => _MarketRunnerChartState();
}
class _MarketRunnerChartState extends State<MarketRunnerChart> {
useEffect(() {
print('your log');
}, []);
#override
Widget build(BuildContext context) {
return Text("Some text");
}
}
You can follow the doc example, import flutter_hooks, extend the HookWidget.
import 'package:flutter_hooks/flutter_hooks.dart';
class Example extends HookWidget {
const Example({Key? key, })
: super(key: key);
#override
Widget build(BuildContext context) {
//your variable/instance like to listen
useEffect(() {
log('okok');
}, [...listenThisInstance...]);
return Container();
}
}
More about useEffect
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
I get the error below when I compile the app.
The following NoSuchMethodError was thrown building MyApp(dirty, state: _MyAppState#73713):
The method 'call' was called on null.
Receiver: null
Tried calling: call(Instance of 'ChangeNotifierProvider<UserLoggedIn>')
The error points me to the 'MyApp' part of the code and so I have no idea how to tackle this one.
My app ran with no error before migrating.
This is a part of my code where the cause for this error is.
I went through the code with and I can't find a possible syntax error.
void main() {
runApp(ProviderScope(child: MyApp()));
}
class MyApp extends StatefulHookWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
bool _amplifyConfigured = false;
bool checkAuthStatus = false;
late AmplifyAuthCognito auth;
var userLoggedIn;
var useProvider;
#override
void initState() {
// TODO: implement initState
super.initState();
_configureAmplify();
}
void _configureAmplify() async {
if (!mounted) return;
auth = AmplifyAuthCognito();
await Amplify.addPlugin(auth);
try {
await Amplify.configure(amplifyconfig);
} on AmplifyAlreadyConfiguredException {
print('Already configured');
}
try {
getUserStatus();
setState(() {
_amplifyConfigured = true;
});
} catch (e) {
print(e);
}
}
#override
Widget build(BuildContext context) {
userLoggedIn = useProvider(userLoggedInProvider);
```
Riverpod removed useProvider in version 1.0.0. As described in the migration guide, you will need to use StatefulHookConsumerWidget instead of StatefulHookWidget to access that same functionality in the newest version:
class MyApp extends StatefulHookConsumerWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends ConsumerState<MyApp> {
// ...
#override
Widget build(BuildContext context) {
userLoggedIn = ref.watch(userLoggedInProvider);
// ...
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.
I am trying to see if an id key is available in my app's shared_pereferences and if there is, redirect my user to the homepage. I am checking the Id in the initState() function of my main.dart and I know that the id exists because I can get it in other pages. but in my main.dart it returns null. any ideas?
here is my main.dart code:
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import './ui/pages/auth/auth_one.dart';
import './ui/pages/main_page.dart';
import 'package:shared_preferences/shared_preferences.dart';
void main() {
SystemChrome.setPreferredOrientations(
[DeviceOrientation.portraitDown, DeviceOrientation.portraitUp])
.then((_) => runApp(MyApp()));
}
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
String userId;
#override
void initState() {
_getUserId().then((id) => userId = id);
super.initState();
}
#override
Widget build(BuildContext context) {
print(userId);
return MaterialApp(
theme: ThemeData(primarySwatch: Colors.deepPurple),
debugShowCheckedModeBanner: false,
home: userId == null ? AuthOne() : MainPage(),
);
}
_getUserId() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
var id = prefs.getString('id');
return id;
}
}
Your _getUserId method is async, so you will have to refresh the widget after you get the result.
Use this:
#override
void initState() {
_getUserId().then((id) {
//calling setState will refresh your build method.
setState(() {
userId = id;
});
});
super.initState();
}
This is happening because you are trying to use the value before its calculated.
you could use timer function for delay