Flutter : Why is my builder not getting reloaded after web service call? - api

I have written the web service call and is being called in the initState() method. A CircularProgressIndicator() is being called if there is no data available. But the progress indicator keeps on rotating even though the web service call is over!
After making a service call, why is my builder not getting reloaded?
I am new to flutter!! Am I going wrong anywhere?
class _DashboardState extends State<Dashboard> {
bool isLoading = false;
Future<List<OrganizationModel>> fetchOrganizationData() async {
isLoading = true;
var response = await http.get(organizationAPI);
if (response.statusCode == 200) {
final items = json.decode(response.body).cast<Map<String, dynamic>>();
orgModelList = items.map<OrganizationModel>((json) {
return OrganizationModel.fromJson(json);
}).toList();
isLoading = false;
return orgModelList;
} else {
isLoading = false;
throw Exception('Failed to load internet');
}
}
#override
void initState() {
this.fetchOrganizationData();
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Dashboard"),
),
body: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [Color(0xFF076A72), Color(0xFF64B672)])),
child: Column(
children: <Widget>[
Container(
color: Color(0xFF34A086),
height: 1,
),
isLoading ? loadProgressIndicator() : Expanded(
child: Padding(
padding: const EdgeInsets.only(left: 40, right: 40),
child: ListView(children: <Widget>[])
---my code goes here---

you must call setState(() => isLoading = false;) so Flutter can update the state of the view, and in doing so It will hide your CircularProgressIndicator.

Exception('Failed to load internet'); <-
not a good idea since you'r calling the fetchOrganizationData() without a try catch block
It would be better to try something like :
class _DashboardState extends State<Dashboard> {
bool isLoading = false;
bool isFailure = false;
List<OrganizationModel> orgModelList; // this was missing
//since your not using the return value ( it's saved in the state directly ) you could not set the return type
fetchOrganizationData() async {
isLoading = true;
isFailure = false;
var response = await http.get(organizationAPI);
if (response.statusCode == 200) {
final items = json.decode(response.body).cast<Map<String, dynamic>>();
orgModelList = items.map<OrganizationModel>((json) {
return OrganizationModel.fromJson(json);
}).toList();
isFailure = false;
// the return is not required
} else {
isFailure = true;
}
isLoading = false;
setState((){}); // by calling this after the whole state been set, you reduce some code lines
//setState is required to tell flutter to rebuild this widget
}
This way you have a isFailure flag saying if something went wrong while fetching.

Related

how to write an API in future async if there is an additional path in it

hi guys im beginner in flutter, i want to write API path in my future async connector API, but there is an additional path in it. how to write it? i got an error. below is the code.
Future<Map> getData() async {
// final String apiURL = ;
var response = await http.get(
'https://api.batulimee.com/v1_ship/port_detail?' +
'port_id=' +
widget.list[widget.index]['port_id'].toString(),
);
return json.decode(response.body) as Map<String, dynamic>;
}
FutureBuilder<Map>(
future: getData(),
builder: (context, snapshot) {
if (snapshot.hasError) print(snapshot.error);
return snapshot.hasData
? new Ports(
list: snapshot.data['data'],
)
: new Container();
},
),
class Ports extends StatelessWidget {
final List list;
Ports({this.list});
#override
Widget build(BuildContext context) {
return ListView.builder(
scrollDirection: Axis.horizontal,
shrinkWrap: true,
itemCount: list.length,
itemBuilder: (context, i) {
return Card(
child: Container(
padding: EdgeInsets.all(8),
color: Colors.red,
child: Text(
list[i]['port_website'].toString(),
style: TextStyle(color: Colors.white, fontSize: 24),
),
),
);
},
);
}
}
and this is the API path.
https://api.batulimee.com/v1_ship/port_detail?port_id=6
this is the error.
type '_InternalLinkedHashMap<String, dynamic>' is not a subtype of type
'List<dynamic>'
The easiest way to concatenate Strings in Flutter is using the $ sign.
Future<Map> getData() async {
// final String apiURL = ;
var response = await http.get(
'https://api.batulimee.com/v1_ship/port_detail?port_id=${widget.list[widget.index]['port_id'].toString()}',
);
return json.decode(response.body);
}
On an unrelated side note, I would suggest putting the http.get inside a try / catch bloc, or handling any possible errors that could come from the response being null or the response.body being null
In flutter you cannot use string concatenation with variables.
The only way for this is to use dart special character $.
If you want just value from variable than just do this:
int a = 1;
String b = 'Some text $a';
If you need value from property of some object than use this way:
SomeObject so = new SomeObject();
String v = 'Value from some property of some object is ${so.value}';
In your situation solution will be:
Future<Map> getData() async {
// final String apiURL = ;
var response = await http.get(
'https://api.batulimee.com/v1_ship/port_detail?port_id=${widget.list[widget.index]["port_id"].toString()}',
);
return json.decode(response.body);
}

how to upload multiple multipart images through API in flutter

I am new to app development & still learning flutter, I used multiple image picker package to pick multiple images from gallery, I am wondering how can I post the list of images to to server using API? I am using MongoDB with AWS. any help is appreciated.
Future<String> uploadImage(filename) async {
var request = http.MultipartRequest('POST', Uri.parse(serverReceiverPath));
request.files.add(await http.MultipartFile.fromPath('file', filename));
var res = await request.send();
print(res.statusCode);
var bodyResponse = await res.stream.bytesToString(); // response body
print(bodyResponse);
}
Below is code to pick images from gallery using multi image picker, I am able to pick images and show them on the screen.
class PickImages extends StatefulWidget {
#override
_PickImagesState createState() => _PickImagesState();
}
class _PickImagesState extends State<PickImages> {
List<Asset> images = List<Asset>();
// String _error = 'No Error Detected';
#override
void initState() {
super.initState();
}
Widget buildGridView() {
return GridView.count(
crossAxisCount: 3,
children: List.generate(images.length, (index) {
Asset asset = images[index];
return AssetThumb(
asset: asset,
width: 300,
height: 300,
);
}),
);
}
Future<void> loadAssets() async {
List<Asset> resultList = List<Asset>();
String error = 'No Error Dectected';
try {
resultList = await MultiImagePicker.pickImages(
maxImages: 300,
enableCamera: true,
selectedAssets: images,
cupertinoOptions: CupertinoOptions(takePhotoIcon: "chat"),
materialOptions: MaterialOptions(
actionBarColor: "#abcdef",
actionBarTitle: "Example App",
allViewTitle: "All Photos",
useDetailsView: false,
selectCircleStrokeColor: "#000000",
),
);
} on Exception catch (e) {
error = e.toString();
}
if (!mounted) return;
setState(() {
images = resultList;
// _error = error;
});
}
#override
Widget build(BuildContext context) {
return Column(
children: [
Container(
height: height(context)*0.35,
width: 400,
child: buildGridView()),
RaisedButton(
child: Text("Pick images"),
onPressed: loadAssets,
),
],
);
}
}

How to set a timer to update API data periodically in Flutter

I'm getting data from an API and load it to a listview. My listview is showing the result of the query without any problem. I want to update my listview by periodically getting data from API. What is the best way to do this?
This is how I'm calling API method in initState of Home Screen.
#override
void initState() {
// TODO: implement initState
super.initState();
_fixtureData = getFixture();
}
Future<List<Fixtures>> getFixture() async {
fixtureList = await FootballApi.getFixtureData();
return fixtureList;
}
This is the code which I render ListView by FutureBuilder in Home Screen.
FutureBuilder<List<Fixtures>>(
future: _fixtureData,
builder: (context, snapshot) {
if (snapshot.hasData) {
fixtureList = snapshot.data;
return AppListView(
matchList: fixtureList,
//callback function brings the matchCounter value from ListView class
onChange: (value) {
setState(() {
matchCounter = value;
});
},
finalBetList: (value) {
setState(() {
betList = value;
});
},
);
}
return Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
SizedBox(
child: CircularProgressIndicator(),
width: 60,
height: 60,
),
const Padding(
padding: EdgeInsets.only(top: 16),
child: Text(
'Awaiting result...',
style: TextStyle(color: Colors.white),
),
)
],
);
},
),
And this snippet is the API method that I call.
static Future<List<Fixtures>> getFixtureData() async {
Map<String, String> queryParameters = {
'league': '79',
'next': '5',
};
http.Response response = await http.get(
getUrl('fixtures', queryParameters),
headers: requestHeaders,
);
if (response.statusCode == 200) {
String data = response.body;
List<dynamic> result = jsonDecode(data)['response'];
for (int i = 0; i < result.length; i++) {
Fixtures fixture = Fixtures();
fixture.leagueID = jsonDecode(data)['response'][i]['league']['id'];
fixture.country = jsonDecode(data)['response'][i]['league']['country'];
fixture.leagueName = jsonDecode(data)['response'][i]['league']['name'];
fixture.fixtureID = jsonDecode(data)['response'][i]['fixture']['id'];
//get Odds to match with fixtures by fixtureID
await getOddsData(fixture.fixtureID);
fixture.dateTime =
DateTime.parse(jsonDecode(data)['response'][i]['fixture']['date']);
fixture.homeTeam =
jsonDecode(data)['response'][i]['teams']['home']['name'];
fixture.awayTeam =
jsonDecode(data)['response'][i]['teams']['away']['name'];
fixture.status =
jsonDecode(data)['response'][i]['fixture']['status']['long'];
fixture.homeGoals = jsonDecode(data)['response'][i]['goals']['home'];
fixture.awayGoals = jsonDecode(data)['response'][i]['goals']['away'];
fixture.htScoreHome =
jsonDecode(data)['response'][i]['score']['halftime']['home'];
fixture.htScoreAway =
jsonDecode(data)['response'][i]['score']['halftime']['away'];
fixture.ftScoreHome =
jsonDecode(data)['response'][i]['score']['fulltime']['home'];
fixture.ftScoreAway =
jsonDecode(data)['response'][i]['score']['fulltime']['away'];
if (oddsList.length > 0) {
for (int j = 0; j < oddsList.length; j++) {
if (oddsList[j].fixtureID == fixture.fixtureID) {
fixture.homeOdds = oddsList[j].homeOdds;
fixture.drawOdds = oddsList[j].drawOdds;
fixture.awayOdds = oddsList[j].awayOdds;
fixture.bookmakerName = oddsList[j].bookmakerName;
FootballApi.fixtureList.add(
fixture);
}
}
}
}
} else {
print('statusCode: ' + response.statusCode.toString());
}
return FootballApi.fixtureList;
}
A timer would be a nice choice.
Start a timer once your state is initialized and dispose of once the widget is disposed.
Inside the timer, you call the API and setState for the widget.
#override
void initState() {
getFixture();
_timer = new Timer.periodic(Duration(seconds: 30),
(_) => getFixture());
super.initState();
}
#override
void dispose() {
_timer.cancel();
super.dispose();
}

Flutter: Keep user logged in after app closes and reopen

I'm a beginner and requesting help. I tried to find the answer but sadly cannot find the answer that I'm looking for. I'm building an app where users can see the homepage and other parts of the app when they are not logged in. This part I have working but only when I close the app and restart it I need to log in again. This is of course not ideal. I tried to look for the SharedPreferences but all examples I can find are with a login function for the whole app and I can't figure out how to get it working in my app. Below is the code I have so far. All help is appreciated and thank you all in advance!
(Apologies for posting so much code but it might help!)
Main.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:reboo1/services/firebaseauthservice.dart';
import 'package:reboo1/services/users.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return StreamProvider<User>.value(
value: AuthService().user,
child: MaterialApp(
title: 'Rèboo',
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home:HomePage(),
),
);
}
}
HomePage.dart
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:reboo1/authenticate/signin.dart';
import 'package:reboo1/authenticate/signout.dart';
import 'package:reboo1/models/drawerlogin.dart';
import 'package:reboo1/models/drawerlogout.dart';
import 'package:reboo1/services/firebaseauthservice.dart';
import 'package:reboo1/services/users.dart';
class DrawerItem {
String title;
IconData icon;
DrawerItem(this.title, this.icon);
}
class HomePage extends StatefulWidget {
final drawerItems = [
new DrawerItem("Rèboo", Icons.home),
new DrawerItem("Inbox", Icons.mail_outline),
new DrawerItem("Profile", Icons.person_outline),
new DrawerItem("Reservations", Icons.event_note),
new DrawerItem("Favorites", Icons.favorite_border),
new DrawerItem("Vouchers", Icons.card_giftcard),
new DrawerItem("Invite Friends", Icons.person_add),
new DrawerItem("Settings", Icons.settings),
new DrawerItem("FAQ", Icons.help_outline),
new DrawerItem("Terms & Conditions", Icons.assignment),
new DrawerItem("Sign out", Icons.exit_to_app),
];
#override
State<StatefulWidget> createState() {
return new HomePageState();
}
}
class HomePageState extends State<HomePage> {
final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
int _selectedDrawerIndex = 0;
var _setIcon = Icons.person;
var drawer;
String uid;
_getDrawerItemWidget(int pos) {
switch (pos) {
case 0:
return new Reboo();
case 1:
return new Inbox();
case 2:
return new Profile();
case 3:
return new Reservations();
case 4:
return new Favorites();
case 5:
return new Vouchers();
case 6:
return new InviteFriends();
case 7:
return new Settings();
case 8:
return new Faq();
case 9:
return new TermsAndConditions();
case 10:
return new SignOut();
default:
return new Text("Error");
}
}
_onSelectItem(int index) {
setState(() => _selectedDrawerIndex = index);
if (_selectedDrawerIndex == 0) {
setState(() => _setIcon = Icons.person);
} else {
setState(() => _setIcon = Icons.arrow_back);
}
Navigator.of(context).pop(); // close the drawer
}
#override
Widget build(BuildContext context) {
var drawerOptions = <Widget>[];
for (var i = 0; i < widget.drawerItems.length; i++) {
var d = widget.drawerItems[i];
drawerOptions.add(new ListTile(
leading: new Icon(
d.icon,
color: Color(0xFF008577),
),
title: new Text(d.title),
selected: i == _selectedDrawerIndex,
onTap: () => _onSelectItem(i),
));
}
final auth = Provider.of<User>(context, listen: false);
if (auth != null) {
drawer = DrawerLogin();
} else {
drawer = DrawerLogout();
}
return Scaffold(
key: _scaffoldKey,
appBar: AppBar(
leading: Builder(
builder: (BuildContext context) {
return IconButton(
icon: new Icon(_setIcon),
onPressed: () {
if (_selectedDrawerIndex == 0) {
Scaffold.of(context).openDrawer();
} else {
setState(() => _selectedDrawerIndex = 0);
setState(() => _setIcon = Icons.person);
}
},
tooltip: MaterialLocalizations.of(context).openAppDrawerTooltip,
);
},
),
centerTitle: true,
title: new Text(widget.drawerItems[_selectedDrawerIndex].title),
backgroundColor: Color(0xFF008577),
elevation: 0.0,
actions: <Widget>[
IconButton(
icon: Icon(Icons.menu, color: Colors.white),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => Company()),
);
},
)
],
),
drawer: new Drawer(
child: SingleChildScrollView(
child: new Column(
children: <Widget>[drawer, new Column(children: drawerOptions)],
),
),
),
body: _getDrawerItemWidget(_selectedDrawerIndex),
);
}
}
FirebaseAuthService.dart
import 'package:firebase_auth/firebase_auth.dart';
import 'package:reboo1/services/users.dart';
import 'package:reboo1/services/database.dart';
class AuthService {
final FirebaseAuth _auth = FirebaseAuth.instance;
// create user obj based on FirebaseUser
User _userFromFirebaseUser(FirebaseUser user){
return user != null ? User(uid: user.uid) : null;
}
// auth change user stream
Stream<User> get user {
return _auth.onAuthStateChanged
.map(_userFromFirebaseUser);
}
// sign with email & password
Future signInWithEmailAndPassword(String email, String password) async {
try {
AuthResult result = await _auth.signInWithEmailAndPassword(email: email, password: password);
FirebaseUser user = result.user;
return _userFromFirebaseUser(user);
}catch(e){
print(e.toString());
return null;
}
}
// register with email & password
Future registerForm(String email, String password, String birthday, String address, String fullName, String phone) async {
try {
AuthResult result = await _auth.createUserWithEmailAndPassword(email: email, password: password);
FirebaseUser user = result.user;
// creat a new document in the database for the user with the uid
await DatabaseService(uid: user.uid).updateUserData(email, birthday, address, fullName, phone);
return _userFromFirebaseUser(user);
}catch(e){
print(e.toString());
return null;
}
}
Stream<String> get onAuthStateChanged =>
_auth.onAuthStateChanged.map(
(FirebaseUser user) => user?.uid,
);
// GET UID
Future<String> getCurrentUID() async {
return (await _auth.currentUser()).uid;
}
Future getCurrentUser() async {
return await _auth.currentUser();
}
//sign out
Future signOut() async{
try{
return await _auth.signOut();
}catch(e){
print(e.toString());
return null;
}
}
}
SignIn.dart
import 'package:flutter/material.dart';
import 'package:reboo1/authenticate/register.dart';
import 'package:reboo1/home/homepage.dart';
import 'package:reboo1/services/constants.dart';
import 'package:reboo1/services/loading.dart';
import 'package:reboo1/services/firebaseauthservice.dart';
import 'package:shared_preferences/shared_preferences.dart';
class SignIn extends StatefulWidget {
final Function toggleView;
SignIn({this.toggleView});
#override
_SignInState createState() => _SignInState();
}
class _SignInState extends State<SignIn> {
final AuthService _auth = AuthService();
final _formKey = GlobalKey<FormState>(); //global key
bool loading = false;
// text field state
String email = '';
String password = '';
String error = '';
#override
Widget build(BuildContext context) {
return loading
? Loading()
: Scaffold(
backgroundColor: Colors.brown[50],
appBar: AppBar(
backgroundColor: Color(0xFF008577),
elevation: 0.0,
title: Text('Sign in to Rèboo'),
actions: <Widget>[
FlatButton.icon(
textColor: Colors.white,
icon: Icon(Icons.person),
label: Text('Register'),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => Register()),
);
},
)
],
),
body: Container(
padding: EdgeInsets.symmetric(vertical: 20.0, horizontal: 50.0),
child: Form(
key: _formKey, //global key
child: Column(children: <Widget>[
SizedBox(height: 20.0),
TextFormField(
decoration: textInputDecoration.copyWith(
hintText: 'Email address'),
validator: (val) =>
val.isEmpty ? 'Enter an email address' : null,
onChanged: (val) {
setState(() => email = val);
}),
SizedBox(height: 20.0),
TextFormField(
decoration:
textInputDecoration.copyWith(hintText: 'Password'),
validator: (val) =>
val.length < 8 ? 'Enter valid password' : null,
obscureText: true,
onChanged: (val) {
setState(() => password = val);
},
),
SizedBox(height: 20.0),
RaisedButton(
color: Colors.purple[400],
child:
Text('Sign in', style: TextStyle(color: Colors.white)),
onPressed: () async {
if (_formKey.currentState.validate()) {
setState(() => loading = true);
dynamic result = await _auth.signInWithEmailAndPassword(
email, password);
if (result == null) {
setState(() {
error =
'Could not sign in with those credentials. Please register an account.';
setState(() => loading = false);
});
} else {
setState(() => loading = false);
Navigator.push(
context,
MaterialPageRoute(builder: (context) => HomePage()),
);
}
}
},
),
SizedBox(height: 12.0),
Text(
error,
style: TextStyle(color: Colors.red, fontSize: 14.0),
)
]),
),
),
);
}
}
Have provided a dummy implementation but this is the legit way to tackle this. If you want to search on google, search for keyword firebase onAuthChanged, and read about it. No local storage is required and the most simple and secure way to handle your requirement.
StreamBuilder<FirebaseUser>(
stream: FirebaseAuth.instance.onAuthStateChanged,
builder: (BuildContext context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return print("getting data");
} else if (snapshot.connectionState == ConnectionState.active &&
!snapshot.hasData) {
return print("no user found");
} else if (snapshot.connectionState == ConnectionState.active &&
snapshot.hasData)
{
return print(snapshot.data.currentuser);
}
else
return null;
},
You could save some sort of data to your shared preferences, then when a user opens the app you could retrieve that data from the shared preferences to determine if the user should already be logged in or not. That data could be the users bearer token and expiry date for instance. This could look something like this:
Save data to shared preferences:
Future storeTokenPropertiesToPreferences(
TokenProperties accessTokenProperties, refreshTokenProperties) async {
final preferences = await SharedPreferences.getInstance();
preferences.setString(
_preferencesLocation,
json.encode(
{
'accessToken': accessTokenProperties?.value,
'refreshToken': refreshTokenProperties?.value,
'accessTokenExpiresAt':
accessTokenProperties?.expiresAt?.toIso8601String(),
'refreshTokenExpiresAt':
refreshTokenProperties?.expiresAt?.toIso8601String(),
},
),
);
Retrieve data:
Future<TokenProperties> _tokenPropertiesFromPreferences(
String tokenName) async {
final _userData = await _getPreferences();
try {
final value = _userData[tokenName];
final expiresAt = DateTime.parse(_userData['${tokenName}ExpiresAt']);
final lifeTime = expiresAt.difference(DateTime.now()).inSeconds;
return (value == null || expiresAt == null)
? null
: TokenProperties(value, lifeTime);
} catch (e) {
await _loggingService.log(LoggingMethod.ERROR,
'Could not get token properties from preferences for token $tokenName. Got exception: $e');
return null;
}
Future<Map<String, Object>> _getPreferences() async {
final preferences = await SharedPreferences.getInstance();
if (!preferences.containsKey(_preferencesLocation)) return null;
return json.decode(preferences.getString(_preferencesLocation))
as Map<String, Object>;
}
You could obviously change out the object properties to whatever you need, this is just an example.
I don't think you need to mess up with local storage to save user's detail.As Firebase itself store user's detail in local storage.You just need to call .currentUser() method of FirebaseAuth class object when your app starts. If the current user is null then load Login Page otherwise your Main Page. Just like that;
Future loadUser() async {
FirebaseUser _tempUser = await _auth.currentUser();
if (_tempUser != null)
// goto MainPage(mainUser:_tempUser);
} else
// goto LoginPage;
}
thanks to #UTKARSH Sharma I found out that the user is not logged out but the problem was in my own code. I found the below code which helped me solve my problem:
#override
void initState() {
super.initState();
var auth = FirebaseAuth.instance;
auth.onAuthStateChanged.listen((user) {
if (user != null) {
print("user is logged in");
//navigate to home page using Navigator Widget
} else {
print("user is not logged in");
//navigate to sign in page using Navigator Widget
}
});
}
Thank you everyone for your help!

Issue loading assets into flutter project

I'm trying to load a TFLite model into flutter but I'm getting the exception "failed to load model". I've loaded the asset through the yaml, imported the TFLite plugin and made sure the file path is right but I keep getting the same exception printed. I've tested the model with python and it works so I'm just trying to get it to work with flutter now.
Code:
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:tflite/tflite.dart';
import 'package:image_picker/image_picker.dart';
import 'package:image/image.dart' as img;
void main() {
runApp(MyApp());
}
const String TF = "image_classifier";
//const String yolo = "Tiny YOLOv2";
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: TfliteHome(),
);
}
}
class TfliteHome extends StatefulWidget {
#override
_TfliteHomeState createState() => _TfliteHomeState();
}
class _TfliteHomeState extends State<TfliteHome> {
String _model = TF;
File _image;
double _imageWidth;
double _imageHeight;
bool _busy = false;
List _recognitions;
#override
void initState() {
super.initState();
_busy = true;
loadModel().then((val) {
setState(() {
_busy = false;
});
});
}
loadModel() async {
Tflite.close();
try {
String res;
res = await Tflite.loadModel(
model: "assets/image_classifier.tflite",
labels: "assets/image_labels.txt",
);
print(res);
} on PlatformException {
print("Failed to load the model");
}
}
selectFromImagePicker() async {
var image = await ImagePicker.pickImage(source: ImageSource.gallery);
if (image == null) return;
setState(() {
_busy = true;
});
predictImage(image);
}
predictImage(File image) async {
if (image == null) return;
if (_model == TF) {
await TFModel(image);
}
FileImage(image)
.resolve(ImageConfiguration())
.addListener((ImageStreamListener((ImageInfo info, bool _) {
setState(() {
_imageWidth = info.image.width.toDouble();
_imageHeight = info.image.height.toDouble();
});
})));
setState(() {
_image = image;
_busy = false;
});
}
TFModel(File image) async {
var recognitions = await Tflite.detectObjectOnImage(
path: image.path,
model: "image_classifier",
threshold: 0.3,
imageMean: 0.0,
imageStd: 255.0,
numResultsPerClass: 3);
setState(() {
_recognitions = recognitions;
});
}
List<Widget> renderBoxes(Size screen) {
if (_recognitions == null) return [];
if (_imageWidth == null || _imageHeight == null) return [];
double factorX = screen.width;
double factorY = _imageHeight / _imageHeight * screen.width;
Color blue = Colors.red;
return _recognitions.map((re) {
return Positioned(
left: re["rect"]["x"] * factorX,
top: re["rect"]["y"] * factorY,
width: re["rect"]["w"] * factorX,
height: re["rect"]["h"] * factorY,
child: Container(
decoration: BoxDecoration(
border: Border.all(
color: blue,
width: 3,
)),
child: Text(
"${re["detectedClass"]} ${(re["confidenceInClass"] * 100).toStringAsFixed(0)}%",
style: TextStyle(
background: Paint()..color = blue,
color: Colors.white,
fontSize: 15,
),
),
),
);
}).toList();
}
#override
Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size;
List<Widget> stackChildren = [];
stackChildren.add(Positioned(
top: 0.0,
left: 0.0,
width: size.width,
child: _image == null ? Text("No Image Selected") : Image.file(_image),
));
stackChildren.addAll(renderBoxes(size));
if (_busy) {
stackChildren.add(Center(
child: CircularProgressIndicator(),
));
}
return Scaffold(
appBar: AppBar(
title: Text("TFLite Demo"),
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.image),
tooltip: "Pick Image from gallery",
onPressed: selectFromImagePicker,
),
body: Stack(
children: stackChildren,
),
);
}
}
Pubspec.yaml:
flutter:
# The following line ensures that the Material Icons font is
# included with your application, so that you can use the icons in
# the material Icons class.
uses-material-design: true
# To add assets to your application, add an assets section, like this:
assets:
- assets/image_classifier.tflite
- assets/image_labels.txt
File Path:
Android
In android/app/build.gradle, add the following setting in android block.
aaptOptions {
noCompress 'tflite'
noCompress 'lite'
}
worked for me!!
I think:
assets:
- assets/image_classifier.tflite // also not the two-space indentation
- assets/image_labels.txt
should be without the 'assets' in the paths.
assets:
- image_classifier.tflite
- image_labels.txt