I'm trying to use ObjectBox as the database in a flutter application. The following is the sample code.
However, while execution I was returned with the error of "_store is not initialized".
class _HomePageState extends State<HomePage> {
...
// ๐ ADD THIS
late Stream<List<ShopOrder>> _stream;
#override
void initState() {
super.initState();
setNewCustomer();
getApplicationDocumentsDirectory().then((dir) {
_store = Store(
getObjectBoxModel(),
directory: join(dir.path, 'objectbox'),
);
setState(() {
// ๐ ADD THIS
_stream = _store
.box<ShopOrder>()
// The simplest possible query that just gets ALL the data out of the Box
.query()
.watch(triggerImmediately: true)
// Watching the query produces a Stream<Query<ShopOrder>>
// To get the actual data inside a List<ShopOrder>, we need to call find() on the query
.map((query) => query.find());
hasBeenInitialized = true;
});
});
}
...
}```
initialize the databases in the main one and then you pass the store to the HomePage, that is why it tells you that error '_store no se inicializa'. You must declare your global store and then you pass it to each view.
late Store _stores;
void main() async {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
#override
_MyState createState() => _MyState();
}
class _MyState extends State<MyApp> {
bool iniciando_store = true;
#override
void initState() {
super.initState();
initPlatformState();
getApplicationDocumentsDirectory().then((directory) {
_stores = Store(
getObjectBoxModel(),
directory: join(directory.path, 'objectbox')
);
setState(() {
iniciando_store = false;
});
});
}
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(create: (context) => ThemeProvider()),
],
child: Consumer<ThemeProvider>(builder: (context, theme, snapshot) {
return MaterialApp(
title: 'Object box title',
home: !iniciando_store
? MyHomePage(
title: "Home", loadingSore: iniciando_store, STORE: _stores)
: MyStatefulWidget());
}),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage(
{Key? key,
required this.title,
required this.loadingSore,
required this.STORE})
: super(key: key);
final String title;
final Store STORE;
final bool loadingSore;
#override
_MyHomePageState createState() => _MyHomePageState();
}
this is the simple way to connect with Object box
Related
I saved the data using SharedPreferences, when my app is starting i try check type of user "client" or "company" after that wil be shown screen.
Future<void> setTypeClient() async {
final _storage = SharedPreferences.getInstance();
final storage = await _storage;
storage.setString('type_user_db', 'client');
}
My app is starting
I have variable
var typeUser = ' ' ; and use it when I determine which screen to display
ะfter I get the data from SharedPreferences, I need to put value in this variable
But i have an error, cause variable remains empty
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../views/home_view.dart';
import '../views/auth/client_auth_view.dart';
import '../views/auth/company_auth_view.dart';
import '../views/auth/company_reg_view.dart';
import '../views/client/client_view.dart';
import '../views/company/company_view.dart';
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return const MyAppBody();
}
}
class MyAppBody extends StatefulWidget {
const MyAppBody({Key? key}) : super(key: key);
#override
State<MyAppBody> createState() => _MyAppBodyState();
}
class _MyAppBodyState extends State<MyAppBody> {
Future<void> getTypeUser() async {
final storage = await SharedPreferences.getInstance();
final type = storage.getString('type_user_db');
setState(() {
typeUser = type!;
});
}
var typeUser = '';
Map<String, Widget> screenByUser = {
'client': const ClientView(),
'company': const CompanyView(),
};
bool isLogOut = true;
#override
void initState() {
if (FirebaseAuth.instance.currentUser != null) {
getTypeUser();
setState(() {
isLogOut = false;
});
super.initState();
} else {
return;
}
}
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Well Plus',
debugShowCheckedModeBanner: false,
routes: {
'/main': (context) => const HomeView(),
'/auth_client': (context) => const AuthClientView(),
'/auth_company': (context) => const AuthCompanyView(),
'/reg_company': (context) => const RegCompanyView(),
'/client': (context) => const ClientView(),
'/company': (context) => const CompanyView(),
},
home: isLogOut ? const HomeView() : screenByUser[typeUser],
);
}
}
update
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../views/home_view.dart';
import '../views/auth/client_auth_view.dart';
import '../views/auth/company_auth_view.dart';
import '../views/auth/company_reg_view.dart';
import '../views/client/client_view.dart';
import '../views/company/company_view.dart';
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return const MyAppBody();
}
}
class MyAppBody extends StatefulWidget {
const MyAppBody({Key? key}) : super(key: key);
#override
State<MyAppBody> createState() => _MyAppBodyState();
}
class _MyAppBodyState extends State<MyAppBody> {
Future<void> getTypeUser() async {
final storage = await SharedPreferences.getInstance();
final type = storage.getString('type_user_db');
setState(() {
typeUser = type!;
});
}
var typeUser = '';
Map<String, Widget> screenByUser = {
'client': const ClientView(),
'company': const CompanyView(),
};
bool get isLogOut => typeUser.isEmpty;
#override
void initState() {
if (FirebaseAuth.instance.currentUser == null) {
getTypeUser();
setState(() {
isLogOut = false;
});
super.initState();
} else {
return;
}
}
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Well Plus',
debugShowCheckedModeBanner: false,
routes: {
'/main': (context) => const HomeView(),
'/auth_client': (context) => const AuthClientView(),
'/auth_company': (context) => const AuthCompanyView(),
'/reg_company': (context) => const RegCompanyView(),
'/client': (context) => const ClientView(),
'/company': (context) => const CompanyView(),
},
home: isLogOut ? const HomeView() : screenByUser[typeUser],
);
}
}
error in
setState(() {
isLogOut = false;
});
error screen
enter image description here
To solve this issue we have to clarify synchronous and asynchronous operations.
Initialating you widget with initState and next build method called one by one in synchronous way but getUser is asynchronous.
This is meant that you specify isLogOut before you setting the typeUser.
Instead of manually setting the isLogOut property just to change it to getter
bool get isLogOut => typeUser.isEmpty;
And you can guarantee the property will be correct all the time when you requests it.
Enjoy coding
I am trying to run webview app which add some headers to website url ('cause I need to get device ID for push notifications service), but I get error:lib/main.dart:63:25: Error: Field '_webViewController' should be initialized because its type 'WebViewController' doesn't allow null.
Maybe someone can fix it? By the way, can someone check this code, what can be better here?
It's my first time creating Flutter app from scratch, so I need your opinio about this code :)
Main.dart code:
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';
import 'package:firebase_core/firebase_core.dart';
import 'notification.dart';
void main() async {
await init();
runApp(const MyApp());
}
Future init() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return const MaterialApp(
home: HomePage(),
);
}
}
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
#override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
String notificationTitle = 'No Title';
String notificationBody = 'No Body';
String notificationData = 'No Data';
#override
void initState() {
final firebaseMessaging = FCM();
firebaseMessaging.setNotifications();
firebaseMessaging.streamCtlr.stream.listen(_changeData);
firebaseMessaging.bodyCtlr.stream.listen(_changeBody);
firebaseMessaging.titleCtlr.stream.listen(_changeTitle);
super.initState();
}
_changeData(String msg) => setState(() => notificationData = msg);
_changeBody(String msg) => setState(() => notificationBody = msg);
_changeTitle(String msg) => setState(() => notificationTitle = msg);
final Completer<WebViewController> _completer =
Completer<WebViewController>();
WebViewController _webViewController;
#override
Widget build(BuildContext context) {
_completer.future.then((controller) {
_webViewController = controller;
Map<String, String> header = {'cookie': 'device-id=12345'};
_webViewController.loadUrl('https://helpmate.lt/', headers: header);
});
return Scaffold(
body: Container(
child:
WebView(
debuggingEnabled: true,
javascriptMode: JavascriptMode.unrestricted,
onWebViewCreated: (controller) async {
_completer.complete(controller);
})),
);
}
}
is there a way to trigger the page navigation when a specific variable in provider class changes?
for example.
I want to navigate to login screen whenever the appLock variable becomes true otherwise do nothing
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await SystemChrome.setPreferredOrientations([
DeviceOrientation.portraitUp,
DeviceOrientation.portraitDown,
]);
runApp(MultiProvider(providers: [
ChangeNotifierProvider(create: (_) => AppLockHelper()),
], child: 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();
context.read<AppLockHelper>().startThread();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData(
scaffoldBackgroundColor: kWhiteColor,
),
supportedLocales: L10n.all,
initialRoute: '/',
onGenerateRoute: RouteGenerator.generateRoute,
);
}
}
and this is my provider class
class AppLockHelper extends ChangeNotifier {
bool _appLocked = false;
bool get appLocked => _appLocked;
final _get = GenericGetApi();
Timer? timer;
void startThread() {
timer = Timer.periodic(
const Duration(seconds: 15), (Timer t) => getAppStatus());
}
void stopThread() {
timer?.cancel();
}
Future<void> getAppStatus() async {
var appStatusResult =
await _get.get(endpoint: EndPointsPool.getAppLockStatus);
appStatusResult.fold((l) {
_appLocked = false;
}, (r) {
AppLock appLockResult = AppLock.fromJson(r);
_appLocked = appLockResult.applocked;
});
notifyListeners();
}
}
Okay the solution would be add a listner in initState.
#override
void initState() {
super.initState();
final myNotifier = context.read<AppLockHelper>();
void listener() {
myNotifier.appLocked
? Navigator.pushNamedAndRemoveUntil(context, "/log-in", (_) => false)
: null;
}
myNotifier.addListener(listener);
}
I am using GetX. I need to listen changes in TextController. The follow code do not work:
class Controller extends GetxController{
final txtList = TextEditingController().obs;
#override
void onInit() {
debounce(txtList, (_) {
print("debouce$_");
}, time: Duration(seconds: 1));
super.onInit();
}
}
Is does not print nothing when I am changing txtList value from UI. I suppose it's because it does not check text field inside txtList.
How to get it work?
You need to pass an RxInterface into debounce to do this via GetX. Just create an RxString and add a listener to the controller then pass the RxString into debounce.
class Controller extends GetxController {
final txtList = TextEditingController();
RxString controllerText = ''.obs;
#override
void onInit() {
txtList.addListener(() {
controllerText.value = txtList.text;
});
debounce(controllerText, (_) {
print("debouce$_");
}, time: Duration(seconds: 1));
super.onInit();
}
}
Then on any page in the app you can pass in that controller into the textfield and it'll print the value after the user stops typing for 1 second.
class Home extends StatelessWidget {
final controller = Get.put(Controller());
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: TextField(controller: controller.txtList), // this will print
),
);
}
}
And if you need that value for anything else it's also always accessible via controller.controllerText.value.
By TextEditingController.text, we can already get changing text input value so it does not need .obs.
To pass parameter for debounce, we should pass value itself : txtList.text. (see here: https://github.com/jonataslaw/getx/blob/master/documentation/en_US/state_management.md)
final txtList = TextEditingController(); // 1. here
#override
void onInit() {
debounce(txtList.text, (_) { // 2. here
print("debouce$_");
}, time: Duration(seconds: 1));
super.onInit();
}
This might work.
=================== added 11/21 ==================
Here's the example. I know the RxString variable seems a duplication for TextEditingController.text, but GetX's debounce function needs RxString type variable as a parameter. I tried to find more elegant way to do this, but I couldn't find anything. Please let me know if somebody knows a better way.
// in controller
late final TextEditingController textController;
final RxString userInput = "".obs;
#override
void onInit() {
super.onInit();
textController = TextEditingController();
userInput.value = textController.text;
textController.addListener(() {
userInput.value = textController.text;
}
);
debounce(userInput, (_) {
print("debouce$_");
}, time: Duration(seconds: 1));
}
check this snippet for example to listen to TextEditingController text change listener
import 'package:flutter/material.dart';
void main() async {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(),
darkTheme: ThemeData.dark(),
home: const HomePage(),
);
}
}
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
#override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
final TextEditingController controller = TextEditingController();
#override
void initState() {
super.initState();
controller.addListener(_printLatestValue);
}
void _printLatestValue() {
print('Second text field: ${controller.text}');
}
#override
void dispose() {
controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: TextField(
controller: controller,
),
);
}
}
I have developed and app and the client want me to store the total time spend by the user inside the app
how can I achieve that
I have tried using this App_usage package in flutter but its showing me Star Activity error
if you guys have any solution please let me know
thanks in advance :)
Have some variable that tracks the start time and end/ pause time of the app and persist the difference. You will have to hook that up to the app lifecycle to listen to events such as pausing/ resuming the app. (e.g. How to handle onPause/onResume in Flutter App?)
Something like this:
class AppLifecycleReactor extends StatefulWidget {
const AppLifecycleReactor({Key key}) : super(key: key);
#override
_AppLifecycleReactorState createState() => _AppLifecycleReactorState();
}
class _AppLifecycleReactorState extends State<AppLifecycleReactor>
with WidgetsBindingObserver {
DateTime startTime;
#override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
}
#override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
#override
void didChangeAppLifecycleState(AppLifecycleState state) {
if (state == AppLifecycleState.resumed) {
startTime = DateTime.now();
}
if (state == AppLifecycleState.detached ||
state == AppLifecycleState.paused) {
var usageTime = DateTime.now().difference(startTime);
// do whatever with the usageTime
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: MyContent(),
);
}
}
like Chris Marx said, you can use the counter to store usage time. and to handle the sync operation to server, you can use shared preferenceq to store data and when the app launched again you do sync(update) with the server.
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(new HomePage());
}
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
String? docId;
addTime() async {
docId = await TimeHomePageUsage.addUserStartTime();
}
#override
void initState() {
// TODO: implement initState
super.initState();
addTime();
}
#override
void dispose() {
// TODO: implement dispose
super.dispose();
TimeHomePageUsage.addUserEndTime(docId);
}
#override
Widget build(BuildContext context) {
return Container(
child: Center(
child: Text('Home Page'),
),
);
}
}
class TimeHomePageUsage {
static Future<String?> addUserStartTime() async {
String? docId;
await FirebaseFirestore.instance
.collection('timeUsage')
.add({'startTime': DateTime.now().toString()})
.then((doc) => print("Start Time added ${docId = doc.id} "))
.catchError((error) => print("Failed to add Start Time: $error"));
return docId;
}
static Future<void> addUserEndTime(String? docId) async {
await FirebaseFirestore.instance
.collection('timeUsage')
.doc(docId)
.update({"endTime": DateTime.now().toString()})
.then((value) => print("End Time added "))
.catchError((error) => print("Failed to add End Time: $error"));
}
}