When I run my flutter app for in iOS (both emulator and physical device), the app is displayed with huge padding from top and bottom. This only happens in iOS, the same app for android runs fine with the normal usual layout. All the app here is limited to this area in the display. I didn't define any padding for app in the code. What is the reason for this?
Here's the code in relevant screen;
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:instagram_clone_flutter/screens/register_screen.dart';
import 'package:instagram_clone_flutter/screens/signup_screen.dart';
import 'package:instagram_clone_flutter/utils/colors.dart';
import 'package:instagram_clone_flutter/utils/global_variable.dart';
import 'package:instagram_clone_flutter/widgets/post_card.dart';
class FeedScreen extends StatefulWidget {
const FeedScreen({Key? key}) : super(key: key);
#override
State<FeedScreen> createState() => _FeedScreenState();
}
class _FeedScreenState extends State<FeedScreen> {
#override
Widget build(BuildContext context) {
final width = MediaQuery.of(context).size.width;
return Scaffold(
backgroundColor:
width > webScreenSize ? webBackgroundColor : mobileBackgroundColor,
appBar: width > webScreenSize
? null
: AppBar(
backgroundColor: mobileBackgroundColor,
centerTitle: false,
title: Text("Iron Capital"),
/*SvgPicture.asset(
'assets/ic_instagram.svg',
color: primaryColor,
height: 32,
),*/
actions: [
IconButton(
icon: const Icon(
Icons.person_add_alt_1_sharp,
color: primaryColor,
),
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => const RegisterScreen(),
),
);
},
),
],
),
body: StreamBuilder(
stream: FirebaseFirestore.instance.collection('posts').snapshots(),
builder: (context,
AsyncSnapshot<QuerySnapshot<Map<String, dynamic>>> snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(
child: CircularProgressIndicator(),
);
}
return ListView.builder(
itemCount: snapshot.data!.docs.length,
itemBuilder: (ctx, index) => Container(
margin: EdgeInsets.symmetric(
horizontal: width > webScreenSize ? width * 0.3 : 0,
vertical: width > webScreenSize ? 15 : 0,
),
child: PostCard(
snap: snapshot.data!.docs[index].data(),
),
),
);
},
),
);
}
}
here's the main.dart file;
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:instagram_clone_flutter/providers/user_provider.dart';
import 'package:instagram_clone_flutter/responsive/mobile_screen_layout.dart';
import 'package:instagram_clone_flutter/responsive/responsive_layout.dart';
import 'package:instagram_clone_flutter/responsive/web_screen_layout.dart';
import 'package:instagram_clone_flutter/screens/login_screen.dart';
import 'package:instagram_clone_flutter/utils/colors.dart';
import 'package:provider/provider.dart';
import 'package:instagram_clone_flutter/models/user.dart' as model;
import 'resources/auth_methods.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// initialise app based on platform- web or mobile
if (kIsWeb) {
await Firebase.initializeApp(
/* options: const FirebaseOptions(
apiKey: "AIzaSyCZ-xrXqD5D19Snauto-Fx_nLD7PLrBXGM",
appId: "1:585119731880:web:eca6e4b3c42a755cee329d",
messagingSenderId: "914283146786",
projectId: "instagram-clone-4cea4",
storageBucket: 'instagram-clone-4cea4.appspot.com'
),*/
);
} else {
await Firebase.initializeApp();
}
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
#override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => UserProvider(),),
],
child: MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Iron Capital',
theme: ThemeData.dark().copyWith(
scaffoldBackgroundColor: mobileBackgroundColor,
),
home: StreamBuilder(
stream: FirebaseAuth.instance.authStateChanges(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.active) {
// Checking if the snapshot has any data or not
if (snapshot.hasData) {
// final model.User user = Provider.of<UserProvider>(context).getUser;
//AuthMethods().signOut();
/*if(user.nft!.length < 2 ){
// await AuthMethods().signOut();
}*/
// if snapshot has data which means user is logged in then we check the width of screen and accordingly display the screen layout
return const ResponsiveLayout(
mobileScreenLayout: MobileScreenLayout(),
webScreenLayout: WebScreenLayout(),
);
} else if (snapshot.hasError) {
return Center(
child: Text('${snapshot.error}'),
);
}
}
// means connection to future hasnt been made yet
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(
child: CircularProgressIndicator(),
);
}
return const LoginScreen();
},
),
),
);
}
}
I'm trying to catch the error when my device has no internet connection. I've built out 2 future methods, 1 to import a json and 1 to look into the database. I have a future builder that's suppose to wait for both futures to finish before building out the grid view but it seems like the offlineFlashCardList is being prematurely called due to the connection error. Any idea how to make it wait for both futures to finish before the snapshot error gets called?
import 'package:flutter/material.dart';
import 'package:flutter/foundation.dart';
import 'package:baby_sound/strings.dart';
import 'package:baby_sound/objects/flashCardList.dart';
import 'package:cached_network_image/cached_network_image.dart';
import 'dart:async' show Future;
import 'dart:convert';
import 'package:baby_sound/database/database.dart';
import 'package:baby_sound/objects/network.dart';
import 'package:http/http.dart' as http;
class GridViewWidget extends StatefulWidget{
#override
createState() => new GridViewState();
}
class GridViewState extends State<GridViewWidget>{
List<FlashCardList> flashCardList;
List<FlashCardList> offlineFlashCardList;
Future<List<FlashCardList>> fetchFlashCardList() async{
debugPrint("before response");
List<FlashCardList> tempFlashCardList;
final response = await http.get('some json url');
//checkConnection(url).then((response){
debugPrint ("after database load, response code: ${response.statusCode}");
if (response.statusCode == 200) {
var data = json.decode(response.body);
var flashCardListData = data["FlashCardList"] as List;
tempFlashCardList = flashCardListData.map<FlashCardList>((json) => FlashCardList.fromJson(json)).toList();
for (int i = 0; i < tempFlashCardList.length; i++){
debugPrint("DBProvider listID: ${await DBProvider.db.getFlashCardList(tempFlashCardList[i].flashCardListID)}, flashCardID: ${tempFlashCardList[i].flashCardListID}");
if (await DBProvider.db.getFlashCardList(tempFlashCardList[i].flashCardListID) == null){
DBProvider.db.newFlashCardList(tempFlashCardList[i]);
debugPrint("Adding ${tempFlashCardList[i].name}}}");
} else {
DBProvider.db.updateFlashCardList(tempFlashCardList[i]);
debugPrint("Updating ${tempFlashCardList[i].name}, getFlashCardList: ${DBProvider.db.getFlashCardList(tempFlashCardList[i].flashCardListID)}");
}
}
flashCardList = tempFlashCardList;
debugPrint("Standard flashCardList Size: ${flashCardList.length}");
}
debugPrint("flashCardList Size Before Return: ${flashCardList.length}");
return flashCardList;
}
Future<List<FlashCardList>> fetchFlashCardListFromDB() async{
offlineFlashCardList = await DBProvider.db.getAllFlashCardListFromDB();
debugPrint("fetchFromDB size: ${offlineFlashCardList.length}");
return offlineFlashCardList;
}
#override
void initState(){
debugPrint ('debug main.dart');
super.initState();
}
#override
Widget build(BuildContext context){
return new Scaffold(
appBar: new AppBar(
centerTitle: true,
title: new Text(Strings.pageTitle),
),
body: FutureBuilder<List<FlashCardList>>(
future: new Future(() async{
await fetchFlashCardList();
await fetchFlashCardListFromDB();
}),
builder: (BuildContext context, AsyncSnapshot<List<FlashCardList>> snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
if (snapshot.hasError) {
debugPrint("Snapshot has error: ${snapshot.error}");
return new GridView.builder(
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 200.0,
childAspectRatio: 0.5),
itemCount: offlineFlashCardList.length,
itemBuilder: (BuildContext context, int index) {
return _getGridItemUI(context, offlineFlashCardList[index]);
});
// return new Center(child: new CircularProgressIndicator());
} else {
debugPrint("Grid ViewBuilder");
return new GridView.builder(
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 200.0,
childAspectRatio:0.5),
itemCount: flashCardList.length,
itemBuilder: (BuildContext context, int index) {
return _getGridItemUI(context, flashCardList[index]);
});
}
}else {
debugPrint("CircularProgress");
return new Center(child: new CircularProgressIndicator());
}
})
);
}
_getGridItemUI(BuildContext context, FlashCardList item){
return new InkWell(
onTap: () {
_showSnackBar(context, item);
},
child: new Card(
child: new Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
new Image(image: new CachedNetworkImageProvider("https://babymozart.org/babymozartq92C9TLa9UMkulL2m81xHdn9u2R92e1e/image/" + item.image)),
/*new Expanded(
child:new Center(
child: new Column(
children: <Widget>[
new SizedBox(height: 8.0),
new Expanded(
child: AutoSizeText(
item.name, maxLines: 1,
)
)
],
),
)
)*/
],
),
elevation: 2.0,
margin: EdgeInsets.all(5.0),
)
);
}
_showSnackBar(BuildContext context, FlashCardList item){
}
}
You can use Future.wait to wait for several Future to be completed.
body: FutureBuilder<List<FlashCardList>>(
future: Future.wait([
fetchFlashCardList(),
fetchFlashCardListFromDB(),
]),
Here's an example based on Alexandre's answer (As I found myself looking for how to handle results):
FutureBuilder(
future: Future.wait([
firstFuture(), // Future<bool> firstFuture() async {...}
secondFuture(),// Future<bool> secondFuture() async {...}
//... More futures
]),
builder: (
context,
// List of booleans(results of all futures above)
AsyncSnapshot<List<bool>> snapshot,
){
// Check hasData once for all futures.
if (!snapshot.hasData) {
return CircularProgressIndicator();
}
// Access first Future's data:
// snapshot.data[0]
// Access second Future's data:
// snapshot.data[1]
return Container();
}
);
I'm trying to set a initial index when launching the application by retrieving this index via the sharedpreferences. But I have this error message:
I/flutter (19911): ══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
I/flutter (19911): The following assertion was thrown building RawGestureDetector(state:
I/flutter (19911): RawGestureDetectorState#438bc(gestures: [tap], behavior: opaque)):
I/flutter (19911): Multiple widgets used the same GlobalKey.
I/flutter (19911): The key [GlobalKey#1b0d6] was used by multiple widgets. The parents of those widgets were:
I/flutter (19911): - Padding(padding: EdgeInsets(16.0, 0.0, 16.0, 0.0), renderObject: RenderPadding#61119 NEEDS-LAYOUT
I/flutter (19911): NEEDS-PAINT DETACHED)
I/flutter (19911): - Padding(padding: EdgeInsets(16.0, 0.0, 16.0, 0.0), renderObject: RenderPadding#b3fbc NEEDS-LAYOUT
I/flutter (19911): NEEDS-PAINT)
I/flutter (19911): A GlobalKey can only be specified on one widget at a time in the widget tree.
Here is my code:
import 'package:flutter/material.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:dynamic_theme/dynamic_theme.dart';
import 'package:my_pau/widgets/tabs/tab_playground.dart';
import 'package:my_pau/widgets/tabs/tab_public_bench.dart';
import 'package:my_pau/widgets/tabs/tab_public_toilet.dart';
import 'package:my_pau/widgets/tabs/tab_car_park.dart';
import 'package:my_pau/widgets/tabs/tab_car_pay_machine.dart';
import 'package:my_pau/widgets/drawer_widget.dart';
import 'package:shared_preferences/shared_preferences.dart';
void main() async {
// Force the layout to Portrait mode
await SystemChrome.setPreferredOrientations(
[DeviceOrientation.portraitUp, DeviceOrientation.portraitDown]);
runApp(new MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
final appTitle = 'My Pau';
ThemeData _buildTheme(Brightness brightness) {
return brightness == Brightness.dark
? ThemeData.dark().copyWith(
textTheme: ThemeData.dark().textTheme.apply(
bodyColor: Colors.white,
displayColor: Colors.white,
fontFamily: 'Basier',
),
primaryColor: Colors.teal,
accentColor: Colors.tealAccent,
primaryColorDark: Colors.teal,
backgroundColor: Colors.black)
: ThemeData.light().copyWith(
textTheme: ThemeData.light().textTheme.apply(
bodyColor: Colors.black,
displayColor: Colors.black,
fontFamily: 'Basier',
),
primaryColor: Colors.teal,
accentColor: Colors.tealAccent,
backgroundColor: Colors.white);
}
return DynamicTheme(
defaultBrightness: Brightness.light,
data: (brightness) => _buildTheme(brightness),
themedWidgetBuilder: (context, theme) {
return MaterialApp(
debugShowCheckedModeBanner: false,
localizationsDelegates: [
// ... app-specific localization delegate[s] here
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
],
supportedLocales: [
const Locale('fr', 'FR'), // French
],
title: appTitle,
theme: theme,
home: MyHomePage(title: appTitle),
);
});
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage>
with SingleTickerProviderStateMixin {
Map<String, int> views = {
'playground' : 0,
'publicToilet' : 1,
'carPark' : 2,
'carPayMachine' : 3,
'publicBench' : 4,
};
final List<Tab> _myTabs = <Tab>[
Tab(icon: Icon(Icons.child_care), text: "Aire de jeux"),
Tab(icon: Icon(Icons.wc), text: "WC publics"),
Tab(icon: Icon(Icons.local_parking), text: "Parkings"),
Tab(icon: Icon(Icons.departure_board), text: "Horodateurs"),
Tab(icon: Icon(Icons.event_seat), text: "Bancs publics"),
];
TabController _tabController;
static const String _sharedPreferencesKeyView = 'view';
String _defaultView;
#override
void initState() {
// _tabController = TabController(vsync: this, length: _myTabs.length, initialIndex: 3); ==> If I put this here it works
_loadDefaultSettings().then((value) {
_tabController = TabController(vsync: this, length: _myTabs.length, initialIndex: views[_defaultView]);
});
super.initState();
}
#override
void dispose() {
_tabController.dispose();
super.dispose();
}
//Loading default settings value on start
Future<void> _loadDefaultSettings() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
setState(() {
_defaultView =
(prefs.getString(_sharedPreferencesKeyView) ?? 'playground');
});
}
#override
Widget build(BuildContext context) {
return DefaultTabController(
length: 5,
child: Scaffold(
appBar: AppBar(
title: Text(widget.title),
bottom: TabBar(
controller: _tabController,
isScrollable: true,
tabs: _myTabs,
),
),
body: TabBarView(
controller: _tabController,
physics: NeverScrollableScrollPhysics(),
children: <Widget>[
TabPlayground(),
TabPublicToilet(),
TabCarPark(),
TabCarPayMachine(),
TabPublicBench(),
],
),
drawer: DrawerWidget(),
),
);
}
}
How do I avoid this?
My goal is to display the correct tab when starting the application according to the value stored in the sharedpreferences;)
I solved my problem using a little trick! I animate the TabController when the build of the widget is complete.
#override
Widget build(BuildContext context) {
WidgetsBinding.instance.addPostFrameCallback((_) => executeAfterWholeBuildProcess());
return DefaultTabController(BuildContext context){...}
void _executeAfterWholeBuildProcess() {
if (views[_defaultView] != null)
_tabController.animateTo(views[_defaultView],
duration: Duration(seconds: 1), curve: Curves.linear);
}
Did anyone successfully implemented WebSocket using json_rpc_2 package?https://pub.dartlang.org/packages/json_rpc_2
I try to present live data, e.g. a ticker, from this API: https://api.hitbtc.com/
Actually, I managed to solve the problem. Here's the code:
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:json_rpc_2/json_rpc_2.dart' as json_rpc;
import 'package:web_socket_channel/io.dart';
class SymbolDetails extends StatelessWidget {
final String symbolId;
SymbolDetails({this.symbolId});
#override
Widget build(BuildContext context) {
var _api = IOWebSocketChannel.connect('wss://api.hitbtc.com/api/2/ws');
var client = json_rpc.Client(_api.cast());
client.sendNotification(
'subscribeTicker',
{'symbol': '$symbolId'},
);
return Scaffold(
appBar: AppBar(
title: Text('$symbolId details'),
),
body: StreamBuilder(
stream: _api.stream,
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.connectionState == ConnectionState.none) {
return Center(
child: Text('Please check your internet connection'),
);
} else if (!snapshot.hasData) {
return Center(child: CircularProgressIndicator());
}
String _snapshotData = snapshot.data;
Map _response = json.decode(_snapshotData);
return ListView(
children: [
ListTile(
title: Text('Ask price:'),
trailing: Text(
'${_response['params']['ask']}',
style: TextStyle(fontWeight: FontWeight.bold),
),
),
ListTile(
title: Text('Bid price:'),
trailing: Text(
'${_response['params']['bid']}',
style: TextStyle(fontWeight: FontWeight.bold),
),
),
],
);
},
),
);
}
}
I came up with a more fleshed-out example that runs on MongoDB. It is also null-safe. Here is the API I came up with:
import 'package:shelf/shelf_io.dart' as shelf_io;
import 'package:shelf_web_socket/shelf_web_socket.dart';
import 'package:web_socket_channel/web_socket_channel.dart';
import 'package:mongo_dart/mongo_dart.dart' as mongo;
void main() async {
final handler = webSocketHandler((WebSocketChannel socket){
final server = Server(socket.cast<String>());
server.registerMethod('hello', () async{
mongo.Db db = mongo.Db('mongodb://127.0.0.1:27017/obj1');
await db.open();
var list = await db.collection('Person').find(null).toList();
// await db.close();
return list;
});
server.listen();
});
final server = await shelf_io.serve(handler, '127.0.0.1', 4042);
print('Serving at ws://${server.address.host}:${server.port}');
}
Here is the Flutter client:
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:json_rpc_2/json_rpc_2.dart' as json_rpc;
import 'package:web_socket_channel/io.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
// This is the theme of your application.
//
// Try running your application with "flutter run". You'll see the
// application has a blue toolbar. Then, without quitting the app, try
// changing the primarySwatch below to Colors.green and then invoke
// "hot reload" (press "r" in the console where you ran "flutter run",
// or simply save your changes to "hot reload" in a Flutter IDE).
// Notice that the counter didn't reset back to zero; the application
// is not restarted.
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatelessWidget {
final String title;
const MyHomePage({super.key, required this.title});
#override
Widget build(BuildContext context) {
var api = IOWebSocketChannel.connect(Uri.parse('ws://127.0.0.1:4042'));
var client = json_rpc.Client(api.cast());
client.sendRequest(
'hello',
);
return Scaffold(
appBar: AppBar(
title: Text(title),
),
body: StreamBuilder(
stream: api.stream,
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.connectionState == ConnectionState.none) {
return const Center(
child: Text('Please check your internet connection'),
);
} else if (!snapshot.hasData) {
return const Center(child: CircularProgressIndicator());
}
String snapshotData = snapshot.data;
Map<String,dynamic> raw = json.decode(snapshotData);List items = raw['result'];
return ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
return ListTile(
title: Text('${items[index]["first_name"].toString()} ${items[index]["last_name"].toString()}'),
);
},
);
},
),
);
}
}
When all goes well, the end result is a formatted list of names.
I'm developing a mobile app using the Flutter framework.
I need to read QR Codes, and I have successfully implemented the Barcode Scan library, based on ZXing to decode one through the camera.
Now I would also like to add the chance to pick an image containing a QR code from the gallery and decoding, without having to go through the camera.
I checked the library I'm using and also this one without finding any reference to such functionality: qrcode_reader, qr.
but in vain.
A solution that would imply serializing and decoding byte by byte an image using pure Dart would be acceptable as well.
As I suggested you in my comment you could try using firebase_ml_visionpackage.
Always remember:
You must also configure Firebase for each platform project: Android
and iOS (see the example folder or
https://codelabs.developers.google.com/codelabs/flutter-firebase/#4
for step by step details).
In this example (taken from the official one, but with a specific plugin version - not the latest one) we use image_picker plugin to get the image from device and then we decode the QRCode.
pubspec.yaml
firebase_ml_vision: 0.2.1
image_picker: 0.4.12+1
detector_painters.dart
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:ui' as ui;
import 'package:firebase_ml_vision/firebase_ml_vision.dart';
import 'package:flutter/material.dart';
enum Detector { barcode, face, label, cloudLabel, text }
class BarcodeDetectorPainter extends CustomPainter {
BarcodeDetectorPainter(this.absoluteImageSize, this.barcodeLocations);
final Size absoluteImageSize;
final List<Barcode> barcodeLocations;
#override
void paint(Canvas canvas, Size size) {
final double scaleX = size.width / absoluteImageSize.width;
final double scaleY = size.height / absoluteImageSize.height;
Rect scaleRect(Barcode barcode) {
return Rect.fromLTRB(
barcode.boundingBox.left * scaleX,
barcode.boundingBox.top * scaleY,
barcode.boundingBox.right * scaleX,
barcode.boundingBox.bottom * scaleY,
);
}
final Paint paint = Paint()
..style = PaintingStyle.stroke
..strokeWidth = 2.0;
for (Barcode barcode in barcodeLocations) {
paint.color = Colors.green;
canvas.drawRect(scaleRect(barcode), paint);
}
}
#override
bool shouldRepaint(BarcodeDetectorPainter oldDelegate) {
return oldDelegate.absoluteImageSize != absoluteImageSize ||
oldDelegate.barcodeLocations != barcodeLocations;
}
}
class FaceDetectorPainter extends CustomPainter {
FaceDetectorPainter(this.absoluteImageSize, this.faces);
final Size absoluteImageSize;
final List<Face> faces;
#override
void paint(Canvas canvas, Size size) {
final double scaleX = size.width / absoluteImageSize.width;
final double scaleY = size.height / absoluteImageSize.height;
final Paint paint = Paint()
..style = PaintingStyle.stroke
..strokeWidth = 2.0
..color = Colors.red;
for (Face face in faces) {
canvas.drawRect(
Rect.fromLTRB(
face.boundingBox.left * scaleX,
face.boundingBox.top * scaleY,
face.boundingBox.right * scaleX,
face.boundingBox.bottom * scaleY,
),
paint,
);
}
}
#override
bool shouldRepaint(FaceDetectorPainter oldDelegate) {
return oldDelegate.absoluteImageSize != absoluteImageSize ||
oldDelegate.faces != faces;
}
}
class LabelDetectorPainter extends CustomPainter {
LabelDetectorPainter(this.absoluteImageSize, this.labels);
final Size absoluteImageSize;
final List<Label> labels;
#override
void paint(Canvas canvas, Size size) {
final ui.ParagraphBuilder builder = ui.ParagraphBuilder(
ui.ParagraphStyle(
textAlign: TextAlign.left,
fontSize: 23.0,
textDirection: TextDirection.ltr),
);
builder.pushStyle(ui.TextStyle(color: Colors.green));
for (Label label in labels) {
builder.addText('Label: ${label.label}, '
'Confidence: ${label.confidence.toStringAsFixed(2)}\n');
}
builder.pop();
canvas.drawParagraph(
builder.build()
..layout(ui.ParagraphConstraints(
width: size.width,
)),
const Offset(0.0, 0.0),
);
}
#override
bool shouldRepaint(LabelDetectorPainter oldDelegate) {
return oldDelegate.absoluteImageSize != absoluteImageSize ||
oldDelegate.labels != labels;
}
}
// Paints rectangles around all the text in the image.
class TextDetectorPainter extends CustomPainter {
TextDetectorPainter(this.absoluteImageSize, this.visionText);
final Size absoluteImageSize;
final VisionText visionText;
#override
void paint(Canvas canvas, Size size) {
final double scaleX = size.width / absoluteImageSize.width;
final double scaleY = size.height / absoluteImageSize.height;
Rect scaleRect(TextContainer container) {
return Rect.fromLTRB(
container.boundingBox.left * scaleX,
container.boundingBox.top * scaleY,
container.boundingBox.right * scaleX,
container.boundingBox.bottom * scaleY,
);
}
final Paint paint = Paint()
..style = PaintingStyle.stroke
..strokeWidth = 2.0;
for (TextBlock block in visionText.blocks) {
for (TextLine line in block.lines) {
for (TextElement element in line.elements) {
paint.color = Colors.green;
canvas.drawRect(scaleRect(element), paint);
}
paint.color = Colors.yellow;
canvas.drawRect(scaleRect(line), paint);
}
paint.color = Colors.red;
canvas.drawRect(scaleRect(block), paint);
}
}
#override
bool shouldRepaint(TextDetectorPainter oldDelegate) {
return oldDelegate.absoluteImageSize != absoluteImageSize ||
oldDelegate.visionText != visionText;
}
}
main.dart
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:async';
import 'dart:io';
import 'package:firebase_ml_vision/firebase_ml_vision.dart';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'detector_painters.dart';
void main() => runApp(MaterialApp(home: _MyHomePage()));
class _MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<_MyHomePage> {
File _imageFile;
Size _imageSize;
dynamic _scanResults;
String _title = 'ML Vision Example';
Detector _currentDetector = Detector.text;
Future<void> _getAndScanImage() async {
setState(() {
_imageFile = null;
_imageSize = null;
});
final File imageFile =
await ImagePicker.pickImage(source: ImageSource.gallery);
if (imageFile != null) {
_getImageSize(imageFile);
_scanImage(imageFile);
}
setState(() {
_imageFile = imageFile;
});
}
Future<void> _getImageSize(File imageFile) async {
final Completer<Size> completer = Completer<Size>();
final Image image = Image.file(imageFile);
image.image.resolve(const ImageConfiguration()).addListener(
(ImageInfo info, bool _) {
completer.complete(Size(
info.image.width.toDouble(),
info.image.height.toDouble(),
));
},
);
final Size imageSize = await completer.future;
setState(() {
_imageSize = imageSize;
});
}
Future<void> _scanImage(File imageFile) async {
setState(() {
_scanResults = null;
});
final FirebaseVisionImage visionImage =
FirebaseVisionImage.fromFile(imageFile);
FirebaseVisionDetector detector;
switch (_currentDetector) {
case Detector.barcode:
detector = FirebaseVision.instance.barcodeDetector();
break;
case Detector.face:
detector = FirebaseVision.instance.faceDetector();
break;
case Detector.label:
detector = FirebaseVision.instance.labelDetector();
break;
case Detector.cloudLabel:
detector = FirebaseVision.instance.cloudLabelDetector();
break;
case Detector.text:
detector = FirebaseVision.instance.textRecognizer();
break;
default:
return;
}
final dynamic results =
await detector.detectInImage(visionImage) ?? <dynamic>[];
setState(() {
_scanResults = results;
if (results is List<Barcode>
&& results[0] is Barcode) {
Barcode res = results[0];
_title = res.displayValue;
}
});
}
CustomPaint _buildResults(Size imageSize, dynamic results) {
CustomPainter painter;
switch (_currentDetector) {
case Detector.barcode:
painter = BarcodeDetectorPainter(_imageSize, results);
break;
case Detector.face:
painter = FaceDetectorPainter(_imageSize, results);
break;
case Detector.label:
painter = LabelDetectorPainter(_imageSize, results);
break;
case Detector.cloudLabel:
painter = LabelDetectorPainter(_imageSize, results);
break;
case Detector.text:
painter = TextDetectorPainter(_imageSize, results);
break;
default:
break;
}
return CustomPaint(
painter: painter,
);
}
Widget _buildImage() {
return Container(
constraints: const BoxConstraints.expand(),
decoration: BoxDecoration(
image: DecorationImage(
image: Image.file(_imageFile).image,
fit: BoxFit.fill,
),
),
child: _imageSize == null || _scanResults == null
? const Center(
child: Text(
'Scanning...',
style: TextStyle(
color: Colors.green,
fontSize: 30.0,
),
),
)
: _buildResults(_imageSize, _scanResults),
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(_title),
actions: <Widget>[
PopupMenuButton<Detector>(
onSelected: (Detector result) {
_currentDetector = result;
if (_imageFile != null) _scanImage(_imageFile);
},
itemBuilder: (BuildContext context) => <PopupMenuEntry<Detector>>[
const PopupMenuItem<Detector>(
child: Text('Detect Barcode'),
value: Detector.barcode,
),
const PopupMenuItem<Detector>(
child: Text('Detect Face'),
value: Detector.face,
),
const PopupMenuItem<Detector>(
child: Text('Detect Label'),
value: Detector.label,
),
const PopupMenuItem<Detector>(
child: Text('Detect Cloud Label'),
value: Detector.cloudLabel,
),
const PopupMenuItem<Detector>(
child: Text('Detect Text'),
value: Detector.text,
),
],
),
],
),
body: _imageFile == null
? const Center(child: Text('No image selected.'))
: _buildImage(),
floatingActionButton: FloatingActionButton(
onPressed: _getAndScanImage,
tooltip: 'Pick Image',
child: const Icon(Icons.add_a_photo),
),
);
}
}
UPDATE for iOS and Android
to address a successful build on iOS I've got to use an even lower version of firebase_ml_vision plugin otherwise you have this error.
pubspec.yaml
# https://github.com/firebase/firebase-ios-sdk/issues/2151
firebase_ml_vision: 0.1.2
image_picker: 0.4.12+1
And I get the error you have so I've got to modify also my classes.
main.dart
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:async';
import 'dart:io';
import 'package:firebase_ml_vision/firebase_ml_vision.dart';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'detector_painters.dart';
void main() => runApp(MaterialApp(home: _MyHomePage()));
class _MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<_MyHomePage> {
File _imageFile;
Size _imageSize;
dynamic _scanResults;
String _title = 'ML Vision Example';
Detector _currentDetector = Detector.barcode;
Future<void> _getAndScanImage() async {
setState(() {
_imageFile = null;
_imageSize = null;
});
final File imageFile =
await ImagePicker.pickImage(source: ImageSource.gallery);
if (imageFile != null) {
_getImageSize(imageFile);
_scanImage(imageFile);
}
setState(() {
_imageFile = imageFile;
});
}
Future<void> _getImageSize(File imageFile) async {
final Completer<Size> completer = Completer<Size>();
final Image image = Image.file(imageFile);
image.image.resolve(const ImageConfiguration()).addListener(
(ImageInfo info, bool _) {
completer.complete(Size(
info.image.width.toDouble(),
info.image.height.toDouble(),
));
},
);
final Size imageSize = await completer.future;
setState(() {
_imageSize = imageSize;
});
}
Future<void> _scanImage(File imageFile) async {
setState(() {
_scanResults = null;
});
final FirebaseVisionImage visionImage =
FirebaseVisionImage.fromFile(imageFile);
FirebaseVisionDetector detector;
switch (_currentDetector) {
case Detector.barcode:
detector = FirebaseVision.instance.barcodeDetector();
break;
case Detector.face:
detector = FirebaseVision.instance.faceDetector();
break;
case Detector.label:
detector = FirebaseVision.instance.labelDetector();
break;
default:
return;
}
final dynamic results =
await detector.detectInImage(visionImage) ?? <dynamic>[];
setState(() {
_scanResults = results;
if (results is List<Barcode>
&& results[0] is Barcode) {
Barcode res = results[0];
_title = res.displayValue;
}
});
}
CustomPaint _buildResults(Size imageSize, dynamic results) {
CustomPainter painter;
switch (_currentDetector) {
case Detector.barcode:
painter = BarcodeDetectorPainter(_imageSize, results);
break;
case Detector.face:
painter = FaceDetectorPainter(_imageSize, results);
break;
case Detector.label:
painter = LabelDetectorPainter(_imageSize, results);
break;
case Detector.cloudLabel:
painter = LabelDetectorPainter(_imageSize, results);
break;
default:
break;
}
return CustomPaint(
painter: painter,
);
}
Widget _buildImage() {
return Container(
constraints: const BoxConstraints.expand(),
decoration: BoxDecoration(
image: DecorationImage(
image: Image.file(_imageFile).image,
fit: BoxFit.fill,
),
),
child: _imageSize == null || _scanResults == null
? const Center(
child: Text(
'Scanning...',
style: TextStyle(
color: Colors.green,
fontSize: 30.0,
),
),
)
: _buildResults(_imageSize, _scanResults),
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(_title),
actions: <Widget>[
PopupMenuButton<Detector>(
onSelected: (Detector result) {
_currentDetector = result;
if (_imageFile != null) _scanImage(_imageFile);
},
itemBuilder: (BuildContext context) => <PopupMenuEntry<Detector>>[
const PopupMenuItem<Detector>(
child: Text('Detect Barcode'),
value: Detector.barcode,
),
const PopupMenuItem<Detector>(
child: Text('Detect Face'),
value: Detector.face,
),
const PopupMenuItem<Detector>(
child: Text('Detect Label'),
value: Detector.label,
),
const PopupMenuItem<Detector>(
child: Text('Detect Cloud Label'),
value: Detector.cloudLabel,
),
const PopupMenuItem<Detector>(
child: Text('Detect Text'),
value: Detector.text,
),
],
),
],
),
body: _imageFile == null
? const Center(child: Text('No image selected.'))
: _buildImage(),
floatingActionButton: FloatingActionButton(
onPressed: _getAndScanImage,
tooltip: 'Pick Image',
child: const Icon(Icons.add_a_photo),
),
);
}
}
detector_painters.dart
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:ui' as ui;
import 'package:firebase_ml_vision/firebase_ml_vision.dart';
import 'package:flutter/material.dart';
enum Detector { barcode, face, label, cloudLabel, text }
class BarcodeDetectorPainter extends CustomPainter {
BarcodeDetectorPainter(this.absoluteImageSize, this.barcodeLocations);
final Size absoluteImageSize;
final List<Barcode> barcodeLocations;
#override
void paint(Canvas canvas, Size size) {
final double scaleX = size.width / absoluteImageSize.width;
final double scaleY = size.height / absoluteImageSize.height;
Rect scaleRect(Barcode barcode) {
return Rect.fromLTRB(
barcode.boundingBox.left * scaleX,
barcode.boundingBox.top * scaleY,
barcode.boundingBox.right * scaleX,
barcode.boundingBox.bottom * scaleY,
);
}
final Paint paint = Paint()
..style = PaintingStyle.stroke
..strokeWidth = 2.0;
for (Barcode barcode in barcodeLocations) {
paint.color = Colors.green;
canvas.drawRect(scaleRect(barcode), paint);
}
}
#override
bool shouldRepaint(BarcodeDetectorPainter oldDelegate) {
return oldDelegate.absoluteImageSize != absoluteImageSize ||
oldDelegate.barcodeLocations != barcodeLocations;
}
}
class FaceDetectorPainter extends CustomPainter {
FaceDetectorPainter(this.absoluteImageSize, this.faces);
final Size absoluteImageSize;
final List<Face> faces;
#override
void paint(Canvas canvas, Size size) {
final double scaleX = size.width / absoluteImageSize.width;
final double scaleY = size.height / absoluteImageSize.height;
final Paint paint = Paint()
..style = PaintingStyle.stroke
..strokeWidth = 2.0
..color = Colors.red;
for (Face face in faces) {
canvas.drawRect(
Rect.fromLTRB(
face.boundingBox.left * scaleX,
face.boundingBox.top * scaleY,
face.boundingBox.right * scaleX,
face.boundingBox.bottom * scaleY,
),
paint,
);
}
}
#override
bool shouldRepaint(FaceDetectorPainter oldDelegate) {
return oldDelegate.absoluteImageSize != absoluteImageSize ||
oldDelegate.faces != faces;
}
}
class LabelDetectorPainter extends CustomPainter {
LabelDetectorPainter(this.absoluteImageSize, this.labels);
final Size absoluteImageSize;
final List<Label> labels;
#override
void paint(Canvas canvas, Size size) {
final ui.ParagraphBuilder builder = ui.ParagraphBuilder(
ui.ParagraphStyle(
textAlign: TextAlign.left,
fontSize: 23.0,
textDirection: TextDirection.ltr),
);
builder.pushStyle(ui.TextStyle(color: Colors.green));
for (Label label in labels) {
builder.addText('Label: ${label.label}, '
'Confidence: ${label.confidence.toStringAsFixed(2)}\n');
}
builder.pop();
canvas.drawParagraph(
builder.build()
..layout(ui.ParagraphConstraints(
width: size.width,
)),
const Offset(0.0, 0.0),
);
}
#override
bool shouldRepaint(LabelDetectorPainter oldDelegate) {
return oldDelegate.absoluteImageSize != absoluteImageSize ||
oldDelegate.labels != labels;
}
}
You can try this:
flutter plugin: qr_code_tools
Pub Link - https://pub.dev/packages/qr_code_tools
Home Page - https://github.com/AifeiI/qr_code_tools
You can try this:
flutter plugin: scan
Pub Link - https://pub.dev/packages/scan
Home Page - https://github.com/flutter-package/flutter_scan
Follow the below steps to build a simple QR scanner and generator app in Flutter:
Step 1: First add the following dependency in your pubspec.yaml file
dependencies:
path_provider: ^1.6.24
qr_flutter: ^3.2.0
barcode_scan_fix: ^1.0.2
Main.dart
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
//Given Title
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
//Given Theme Color
theme: ThemeData(
primarySwatch: Colors.indigo,
),
//Declared first page of our app
home: HomePage(),
);
}
}
HomePage.dart
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
width: 500,
height: 500,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
//Display Image
Image(image: NetworkImage("https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQQyYwscUPOH_qPPe8Hp0HAbFNMx-TxRFubpg&usqp=CAU")),
//First Button
FlatButton(
padding: EdgeInsets.all(15),
onPressed: (){
Navigator.of(context).push(MaterialPageRoute(builder: (context)=> ScanQR()));
},
child: Text("Scan QR Code",style: TextStyle(color: Colors.indigo[900]),),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
side: BorderSide(color: Colors.indigo[900]),
),
),
SizedBox(height: 10),
//Second Button
FlatButton(
padding: EdgeInsets.all(15),
onPressed: (){
Navigator.of(context).push(MaterialPageRoute(builder: (context)=>
GenerateQR()));
},
child: Text("Generate QR Code", style: TextStyle(color: Colors.indigo[900]),),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
side: BorderSide(color: Colors.indigo[900]),
),
),
],
),
)
);
}
}
ScanQR.dart
import 'package:barcode_scan_fix/barcode_scan.dart';
import 'package:flutter/material.dart';
class ScanQR extends StatefulWidget {
#override
_ScanQRState createState() => _ScanQRState();
}
class _ScanQRState extends State<ScanQR> {
String qrCodeResult = "Not Yet Scanned";
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Scan QR Code"),
),
body: Container(
padding: EdgeInsets.all(20),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
//Message displayed over here
Text(
"Result",
style: TextStyle(fontSize: 25.0, fontWeight: FontWeight.bold),
textAlign: TextAlign.center,
),
Text(
qrCodeResult,
style: TextStyle(
fontSize: 20.0,
),
textAlign: TextAlign.center,
),
SizedBox(
height: 20.0,
),
//Button to scan QR code
FlatButton(
padding: EdgeInsets.all(15),
onPressed: () async {
String codeSanner = await BarcodeScanner.scan(); //barcode scanner
setState(() {
qrCodeResult = codeSanner;
});
},
child: Text("Open Scanner",style: TextStyle(color: Colors.indigo[900]),),
//Button having rounded rectangle border
shape: RoundedRectangleBorder(
side: BorderSide(color: Colors.indigo[900]),
borderRadius: BorderRadius.circular(20.0),
),
),
],
),
),
);
}
}
GenerateQR.dart
import 'package:flutter/material.dart';
import 'package:qr_flutter/qr_flutter.dart';
class GenerateQR extends StatefulWidget {
#override
_GenerateQRState createState() => _GenerateQRState();
}
class _GenerateQRState extends State<GenerateQR> {
String qrData="https://github.com/ChinmayMunje";
final qrdataFeed = TextEditingController();
#override
Widget build(BuildContext context) {
return Scaffold(
//Appbar having title
appBar: AppBar(
title: Center(child: Text("Generate QR Code")),
),
body: Container(
padding: EdgeInsets.all(20),
child: SingleChildScrollView(
//Scroll view given to Column
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
QrImage(data: qrData),
SizedBox(height: 20),
Text("Generate QR Code",style: TextStyle(fontSize: 20),),
//TextField for input link
TextField(
decoration: InputDecoration(
hintText: "Enter your link here..."
),
),
Padding(
padding: const EdgeInsets.all(8.0),
//Button for generating QR code
child: FlatButton(
onPressed: () async {
//a little validation for the textfield
if (qrdataFeed.text.isEmpty) {
setState(() {
qrData = "";
});
} else {
setState(() {
qrData = qrdataFeed.text;
});
}
},
//Title given on Button
child: Text("Generate QR Code",style: TextStyle(color: Colors.indigo[900],),),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
side: BorderSide(color: Colors.indigo[900]),
),
),
),
],
),
),
),
);
}
}