Flutter barcode scanner on mobile web app - flutter

I have a barcode scanner working fine on Android, but I am struggling to find plugins that support a web app.
This is the closest one I've found that seems to be getting somewhere:
https://pub.dev/packages/ai_barcode
But I can't really get anything to happen.
Here is the code I'm using currently:
import 'package:flutter/material.dart';
import 'package:ai_barcode/ai_barcode.dart';
class WebBarcodeScannerPage extends StatefulWidget {
// void resultCallback (String result) {
// debugtext
// }
#override
_WebBarcodeScannerPageState createState() => _WebBarcodeScannerPageState();
}
class _WebBarcodeScannerPageState extends State<WebBarcodeScannerPage> {
ScannerController _scannerController;
String _debugText = 'debug';
#override
void initState () {
super.initState();
_scannerController = ScannerController(scannerResult: (r) => resultCallback(r));
// _scannerController = ScannerController(scannerResult: (result) {
// resultCallback(result);
// }, scannerViewCreated: () {
// final TargetPlatform platform = Theme.of(context).platform;
// if (TargetPlatform.iOS == platform) {
// Future.delayed(const Duration(seconds: 2), () {
// _scannerController
// ..startCamera()
// ..startCameraPreview();
// });
// } else {
// _scannerController
// ..startCamera()
// ..startCameraPreview();
// }
// });
}
resultCallback (String r) {
print(r);
setState(() {
_debugText = r;
});
}
_body () {
return Column(
children: [
Text(_debugText),
TextButton(
child: Text('Start camera'),
onPressed: () {
_scannerController.startCamera();
},
),
TextButton(
child: Text('Start preview'),
onPressed: () {
_scannerController.startCameraPreview();
},
),
TextButton(
child: Text('Stop camera'),
onPressed: () {
_scannerController.stopCamera();
},
),
TextButton(
child: Text('Stop preview'),
onPressed: () {
_scannerController.stopCameraPreview();
},
),
PlatformAiBarcodeScannerWidget(platformScannerController: _scannerController),
],
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: _body(),
);
}
}
The camera preview shows up in a window, but I can't scan any barcodes or QR codes. The debug text never changes.
Again, my goal is to be able to read barcodes into a string on a mobile web app.

You can use our product Cognex Mobile Barcode SDK
Download page for all supported platforms - https://cmbdn.cognex.com/download#Platforms
Knowledge Base for more information about the integration - https://cmbdn.cognex.com/v2.6.x/knowledge/flutter/license-keys
https://pub.dev/packages/cmbsdk_flutter
Regards,

You should add jsQR.js to web folder.
You can find that HERE

you can use the source example from ai_barcode.
QRCodeDartScanView(
typeCamera: TypeCamer.front,
scanInvertedQRCode: true,
resolutionPreset: QRCodeDartScanResolutionPreset.ultraHigh,
formats: const [
BarcodeFormat.QR_CODE,
],
onCapture: (Result result) {
printInfo(info: result.text);
},
);
Just put this piece of code inside a build method. The camera opens and read qr codes.

simple_barcode_scanner (which leverages html5-qrcode for web apps) worked best for my PWA, in fact it was the only package of several I tried that met my requirements of being able to read both EAN-13 and GS1 data matrix from a web app -- and it worked immediately the first time, and it does not require editing index.html. (I couldn't use ai_barcode due to a hard-to-resolve dependency conflict between ai_barcode and barcode, and barcode is a must-have for my web app -- but even without the dependency issue I struggled to make ai_barcode work reliably).
If you decide to use simple_barcode_scanner note that it does not work on desktop browsers, only mobile browsers, so you'll need a check like this:
import 'package:flutter/foundation.dart';
import 'package:simple_barcode_scanner/simple_barcode_scanner.dart';
...
final isWebMobile = kIsWeb &&
(defaultTargetPlatform == TargetPlatform.iOS ||
defaultTargetPlatform == TargetPlatform.android);
if (isWebMobile) {
String scanResult = await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const SimpleBarcodeScannerPage()),
);
print('scan result = $scanResult');
} else {
print('Scanning is only supported for mobile web browsers.');
}

Related

How can I integrate Google FIT in Flutter app?

I want steps count and calories burned data in my flutter app. I am using health: ^3.1.1+1 package but I'm getting "Authorization not granted" even after giving all permission. I even used permission handler for permission and I was successfully getting permission with permission handler, still I am not getting data from health package. Please help me with the process to authorize my app to fetch data from Google Fit API.
I have successfully generate my OAuth client id from google console and added the json file in my project. Please let me know if there is any other place where I need to add my client id.
I am using given below sample code provided in with the package.
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:health/health.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
enum AppState {
DATA_NOT_FETCHED,
FETCHING_DATA,
DATA_READY,
NO_DATA,
AUTH_NOT_GRANTED
}
class _MyAppState extends State<MyApp> {
List<HealthDataPoint> _healthDataList = [];
AppState _state = AppState.DATA_NOT_FETCHED;
#override
void initState() {
super.initState();
}
/// Fetch data from the healt plugin and print it
Future fetchData() async {
// get everything from midnight until now
DateTime startDate = DateTime(2020, 11, 07, 0, 0, 0);
DateTime endDate = DateTime(2025, 11, 07, 23, 59, 59);
HealthFactory health = HealthFactory();
// define the types to get
List<HealthDataType> types = [
HealthDataType.STEPS,
HealthDataType.WEIGHT,
HealthDataType.HEIGHT,
HealthDataType.BLOOD_GLUCOSE,
HealthDataType.DISTANCE_WALKING_RUNNING,
];
setState(() => _state = AppState.FETCHING_DATA);
// you MUST request access to the data types before reading them
bool accessWasGranted = await health.requestAuthorization(types);
int steps = 0;
if (accessWasGranted) {
try {
// fetch new data
List<HealthDataPoint> healthData =
await health.getHealthDataFromTypes(startDate, endDate, types);
// save all the new data points
_healthDataList.addAll(healthData);
} catch (e) {
print("Caught exception in getHealthDataFromTypes: $e");
}
// filter out duplicates
_healthDataList = HealthFactory.removeDuplicates(_healthDataList);
// print the results
_healthDataList.forEach((x) {
print("Data point: $x");
steps += x.value.round();
});
print("Steps: $steps");
// update the UI to display the results
setState(() {
_state =
_healthDataList.isEmpty ? AppState.NO_DATA : AppState.DATA_READY;
});
} else {
print("Authorization not granted");
setState(() => _state = AppState.DATA_NOT_FETCHED);
}
}
Widget _contentFetchingData() {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Container(
padding: EdgeInsets.all(20),
child: CircularProgressIndicator(
strokeWidth: 10,
)),
Text('Fetching data...')
],
);
}
Widget _contentDataReady() {
return ListView.builder(
itemCount: _healthDataList.length,
itemBuilder: (_, index) {
HealthDataPoint p = _healthDataList[index];
return ListTile(
title: Text("${p.typeString}: ${p.value}"),
trailing: Text('${p.unitString}'),
subtitle: Text('${p.dateFrom} - ${p.dateTo}'),
);
});
}
Widget _contentNoData() {
return Text('No Data to show');
}
Widget _contentNotFetched() {
return Text('Press the download button to fetch data');
}
Widget _authorizationNotGranted() {
return Text('''Authorization not given.
For Android please check your OAUTH2 client ID is correct in Google Developer Console.
For iOS check your permissions in Apple Health.''');
}
Widget _content() {
if (_state == AppState.DATA_READY)
return _contentDataReady();
else if (_state == AppState.NO_DATA)
return _contentNoData();
else if (_state == AppState.FETCHING_DATA)
return _contentFetchingData();
else if (_state == AppState.AUTH_NOT_GRANTED)
return _authorizationNotGranted();
return _contentNotFetched();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Plugin example app'),
actions: <Widget>[
IconButton(
icon: Icon(Icons.file_download),
onPressed: () {
fetchData();
},
)
],
),
body: Center(
child: _content(),
)),
);
}
}
Go to Google Cloud Platform > Apis & Services > OAuth consent screen and check if your app is in Testing mode and add gmails to allow user to use the Oauth
Remember to upload the android debug key sha1!
New version of the health package solved my problem.

How to write platform specific code for web in flutter?

In flutter, we use platform channels that allows us to call platform-specific APIs whether available in Kotlin or Java code on Android, or in Swift or Objective-C code on iOS.
How to achieve the same with web in flutter ? How can one use a npm package and write some javascript code and send the result to flutter? Is this even possible ? There is official docs for writing platform specific code for Android and iOS, but I couldn't find any docs for writing platform specific code for web.
Also, I tried using the js package. If this is the one that has to be used for this case, how to use it ?
This is what I do to display Hubspot chat on Flutter Web.
I have a folder for Hubspot with:
index.html
script.js
style.css
Then a Flutter Widget with webview_flutter_plus plugin:
class HubspotWebview extends StatefulWidget {
#override
_HubspotWebviewState createState() => _HubspotWebviewState();
}
class _HubspotWebviewState extends State<HubspotWebview> {
final _javascriptChannels = Set<JavascriptChannel>();
bool loading = true;
#override
void initState() {
super.initState();
_javascriptChannels.add(JavascriptChannel(
onMessageReceived: (JavascriptMessage message) {
debugPrint('message: ' + message.message);
_toggleLoading();
},
name: 'Loading'));
}
#override
Widget build(BuildContext context) {
final path = '${kIsWeb ? 'assets/' : ''}assets/hubspot_web_page/index.html';
final key = 'web_bot_key';
if (kIsWeb) {
ui.platformViewRegistry.registerViewFactory(
key,
(int viewId) => IFrameElement()
..width = '640'
..height = '360'
..src = path
..style.border = 'none'
..onLoadedData.listen((event) {
_toggleLoading();
}));
}
return Scaffold(
appBar: new AppBar(
backgroundColor: MyColors.blue_business,
title: MyText.subtitle(
getText('business_help_chat', backUpText: 'Help Chat'),
color: MyColors.white_rice,
)),
body: Stack(
children: [
kIsWeb
? HtmlElementView(viewType: key)
: WebViewPlus(
javascriptMode: JavascriptMode.unrestricted,
initialUrl: path,
javascriptChannels: _javascriptChannels,
),
if (loading)
Center(child: CircularProgressIndicator()),
],
),
);
}
void _toggleLoading() => setState(() => loading = !loading);
}
On Javascript file Loading.postMessage('') triggers toggleLoading() on Flutter:
function onConversationsAPIReady() {
window.hsConversationsSettings = {
inlineEmbedSelector: '#hubspot-conversations-inline-parent',
enableWidgetCookieBanner: true,
disableAttachment: true
};
window.history.pushState({}, 'bot', '?bot=true');
window.HubSpotConversations.widget.refresh({openToNewThread: true});
Loading.postMessage('');
}
if (window.HubSpotConversations) {
onConversationsAPIReady();
} else {
window.hsConversationsOnReady = [onConversationsAPIReady];
}

How to prevent multiple network calls from a flutter bloc?

I am building an app using flutter and I'm using the flutter_bloc package for managing state. I have a simple Avatar widget used for displaying for displaying profile photo of a user.
/// A widget for displaying a user profile avatar.
class UserAvatar extends StatelessWidget {
/// The unique identifier for a particular user.
final int userId;
/// The size of the avatar.
final double radius;
UserAvatar({required this.userId, this.radius = 30}) {}
#override
Widget build(BuildContext context) {
context.read<UserInfoBloc>().add(UserInfoEvent.getUserInfo(userId));
return BlocBuilder<UserInfoBloc, UserInfoState>(
builder: (context, state) {
return state.when(initial: () {
return Text('Initial');
}, loading: () {
return Text('Loading');
}, loaded: (user) {
print(user);
return Container(
child: Avatar(
shape: AvatarShape.circle(radius),
loader: Center(
child: ClipOval(
child: Skeleton(
height: radius * 2,
width: radius * 2,
),
),
),
useCache: true,
sources: [NetworkSource(user.avatarUrl ?? '')],
name: user.firstName!.trim(),
onTap: () {
//TODO implement Navigation to profile page
},
),
);
}, error: () {
return Text('error');
});
},
);
}
}
My problem is that the widget will be used multiple times (when displaying a feed and there are contents by the same user). I initially just have the id of the user, then I try to make a network call and try to get the user. I've implemented some form of caching in my repository:
#LazySingleton(as: UserInfoRepository)
class UserInfoRepositoryImpl extends UserInfoRepository {
final GetUserInfoRemoteDataSource remoteDataSource;
final GetUserInfoLocalDataSource localDataSource;
UserInfoRepositoryImpl(
{required this.remoteDataSource, required this.localDataSource});
#override
Future<Either<Failure, User>> getUserInfo(int id) async {
try {
final existsInCache = localDataSource.containsUserModel(id);
if (existsInCache) {
return right(localDataSource.getUserModel(id));
} else {
final result = await remoteDataSource.getUserInfo(id);
localDataSource.cacheUserModel(result);
return right(result);
}
} on ServerExceptions catch (e) {
return left(e.when(() => Failure(),
requestCancelled: () => Failure.requestCancelled(),
unauthorisedRequest: () => Failure.unauthorisedRequest(),
badRequest: (e) => Failure.badRequest(e),
notFound: () => Failure.notFound(),
internalServerError: () => Failure.internalServerError(),
receiveTimeout: () => Failure.receiveTimeout(),
sendTimeout: () => Failure.sendTimeout(),
noInternetConnection: () => Failure.noInternetConnection()));
} on CacheException {
return left(Failure.cacheFailure());
}
}
}
Of course, I used the injectable package for dealing with my dependencies, and I've used the #LazySingleton annotation on the repository. But unfortunately, if I try to display two avatars of the same user, two separate network calls will be made. Of course, I don't want that. How can I solve this problem?

Perform in app force update using current and required build numbers

I want to force update my app.
Here's what I have done so far.
Obtained the current build version of my app using package_info_plus
Obtained the enforced build version which I have stored in the firebase remote config. So I used this package: firebase_remote_config
I then compared the two build numbers to see if the update is needed. What should I do after that?
Here's my code:
void initState(){
super.initState();
checkForUpdate();
_initPackageInfo();
_enforcedVersion();
if(int.parse(_packageInfo.buildNumber) > int.parse(enforcedBuildNumber))
{
//How to force update?
}
}
Future<void> _initPackageInfo() async {
final info = await PackageInfo.fromPlatform();
setState(() {
_packageInfo = info;
});
}
Future<void> _enforcedVersion() async {
final RemoteConfig remoteConfig = RemoteConfig.instance;
await remoteConfig.setConfigSettings(RemoteConfigSettings(
fetchTimeout: const Duration(seconds: 10),
minimumFetchInterval: Duration.zero,
));
await remoteConfig.fetchAndActivate();
setState(() {
enforcedBuildNumber = remoteConfig.getString('enforced_build_number');
});
}
You could display a non dismissable dialog which would ask the user to update the application with a redirection button to the device appstore.
By using a package such as url_launcher you can easily do that:
Code Sample
import 'dart:io' show Platform;
import 'package:url_launcher/url_launcher.dart';
// You can show a dialog like this
showDialog(
context: context,
barrierDismissible: false,
builder: (_) => AlertDialog(
title: Text('Please update your app'),
actions: [
TextButton(
onPressed: launchAppStore,
child: Text('Open App Store'),
),
],
),
);
// Method to open the appstore
void launchAppStore() {
/// Depending on where you are putting this method you might need
/// to pass a reference from your _packageInfo.
final appPackageName = _packageInfo.packageName;
if (Platform.isAndroid) {
launch("https://play.google.com/store/apps/details?id=$appPackageName");
} else if (Platform.isIOS) {
launch("market://details?id=$appPackageName");
}
}

authStateChanges only work if in debug mode

I have a flutter app and everything was fine until i want to release it.
I'm using firebase for auth.
I'm using:
firebase_core: ^0.7.0
firebase_auth: ^0.20.0
In debug mode or in release, my firebase auth login works fine. My problem is after that.
I have a decentralized 'listener' to firebaseAuth.authStateChanges. Here is where i control my app authentication. This is my buildSelf$ function in my auth repository(where i build the auth state listener):
ReplayConnectableStream<AuthState> buildSelf$() {
return Rx.concat([
Stream.value(AuthInit()),
firebaseAuth.authStateChanges().switchMap((firebaseUser) {
print('[AuthRepository][buildSelf][authStateChanges] firebaseUser');
print(firebaseUser);
if (firebaseUser == null) {
return Stream.value(Unauthenticated());
}
/* continue function */
return Authenticated(firebaseUser: firebaseUser);
})
]).publishReplay();
}
buildSelf$ is a method for my AuthRepository. And i initialize it on:
AuthRepository._() {
Firebase.initializeApp().then((app) {
_firebaseAuth = FirebaseAuth.instance;
state$ = buildSelf$();
state$.connect();
});
setPackageInfo();
}
static final AuthRepository instance = AuthRepository._();
All this code is inside my AuthRepository.
The problem is:
When i'm running my app in debug mode. Every thing works fine. I can login and my app (my navigator observer uses the auth repository state$) and i'm redirected to home page. [Here a printscreen from terminal in debug mode. success authStateChanges emit
But when i'm running im release mode, my login response shows in my terminal 'success' but my listener does not show state changes. Here a printscreen from terminal in release mode. authStateChanges emit only 1 time(when opening)
I'm realy lost about whats going on. I tried even to call this authStateChanges directly in my app.dart but still the same way(in release, only checking auth state once).
Solution:
After 3 days of headache, i finally found out my problem:
My app does not initialize firebase app on root (main). So i have to initialize it every time i need to use some firebase package.
I believe that every time one app was initialized, the others failed.
I move my firebase.initializeApp() to my main function and remove every other firebase initialize. Then, everything works fine.
This is my main:
void main() async {
/* ... main code ... */
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(App());
}
I did implement a project with the presented methods in release mode in my iPhone but wasn't able to reproduce the problem, the implementation I did to reproduce the project was:
class _TestFirebaseState extends State<TestFirebase> {
bool userLogado = false;
_loginLogout() {
if (FirebaseAuth.instance.currentUser == null) {
FirebaseAuth.instance.signInWithEmailAndPassword(
email: 'teste1#teste.com', password: '123456');
} else {
FirebaseAuth.instance.signOut();
}
}
#override
void initState() {
super.initState();
final stream = Rx.concat(
[
Stream.value('começou'),
FirebaseAuth.instance.authStateChanges().switchMap<String>(
(user) {
debugPrint('user: ' + user.toString());
if (user == null) {
return Stream.value('unauth');
} else {
return Stream.value('auth');
}
},
),
],
).publishReplay();
stream.listen((value) {
debugPrint('value: ' + value);
this.setState(() {
this.userLogado = value == 'auth';
});
});
stream.connect();
}
#override
Widget build(BuildContext context) {
String text = "sair";
if (!userLogado) {
text = "entrar";
}
return Scaffold(
body: Container(
width: double.infinity,
height: double.infinity,
child: Center(
child: RaisedButton(
onPressed: _loginLogout,
child: Text(text),
),
),
),
);
}
}
the only detail is that the main component that loads everything in the app has a future builder that awaits the firebase to init:
Widget build(BuildContext context) {
return MaterialApp(
home: FutureBuilder(
// Initialize FlutterFire:
future: Firebase.initializeApp(),
builder: (context, snapshot) {
// Check for errors
if (snapshot.hasError) {
return Scaffold(
body: Center(
child: Text(
'ocorreu um problema inicializando o aplicativo...',
),
),
);
}
// Once complete, show your application
if (snapshot.connectionState == ConnectionState.done) {
// return LoginPage();
// return VerifySignIn(homePage: Home());
return TestFirebase();
}
// Otherwise, show something whilst waiting for initialization to complete
return Scaffold(
body: Center(
child: CircularProgressIndicator(),
),
);
},
),
debugShowCheckedModeBanner: false,
);
}
And another detail is that the firebase configuration is implemented following the flutter tutorial for the platform used.
So in order to evaluate if the app configuration is just fine, I would implement that button app inside the same project so you can evaluate it properly.