The idea is that a list appears on my app screen and is updated as I add texts to the textField, but in my tests the list appears empty even though I put test items there.
image error
flutter version 1.0.0
dependences
path_provider: ^1.1.0
code atualization
import 'package:flutter/material.dart';
import 'package:path_provider/path_provider.dart';
import 'dart:io';
import 'dart:async';
import 'dart:convert';
class Home extends StatefulWidget {
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
List _listaTarefas = [];
Future<File> _pegarArquivo() async {
final diretorio = await getApplicationDocumentsDirectory();
return File("${diretorio.path}/dados.json");
}
_salvarArquivo() async {
var arquivo = await _pegarArquivo();
Map<String, dynamic> tarefa = Map();
tarefa['titulo'] = "Teste";
tarefa['realizada'] = false;
_listaTarefas.add(tarefa);
String dados = json.encode(_listaTarefas);
arquivo.writeAsString(dados);
}
_lerArquivo() async {
try {
final arquivo = await _pegarArquivo();
return arquivo.readAsString();
} catch (e) {
return null;
}
}
#override
void initState() {
super.initState();
_lerArquivo().then((dados) {
setState(() {
_listaTarefas = json.decode(dados);
});
});
}
#override
Widget build(BuildContext context) {
_salvarArquivo();
print('Resultado' + _listaTarefas.toString());
return Scaffold(
appBar: AppBar(
title: Text('Lista de tarefas'),
backgroundColor: Colors.purple,
),
floatingActionButtonLocation: FloatingActionButtonLocation.endFloat,
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
backgroundColor: Colors.purple,
onPressed: () {
showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: Text('Adicionar Tarefa'),
content: TextField(
decoration: InputDecoration(
labelText: 'Digite sua tarefa',
),
onChanged: (text) {},
),
actions: <Widget>[
FlatButton(
onPressed: () => Navigator.pop(context),
child: Text('cancelar'),
),
FlatButton(
onPressed: () {
Navigator.pop(context);
},
child: Text('Salvar'),
),
],
);
});
}),
body: Column(
children: <Widget>[
Expanded(
child: ListView.builder(
itemCount: _listaTarefas.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(_listaTarefas[index]['titulo']),
);
}))
],
),
);
}
}
The problem
When you declare the following method
_lerArquivo() async {
try {
final arquivo = await _pegarArquivo();
return arquivo.readAsStringSync();
} catch (e) {
return Text('erro');
}
}
you're returning a String when you do
return arquivo.readAsStringSync();
and a Text when you do
return Text('erro');
The solution
To fix this, you can either
return a string in the second return:
_lerArquivo() async {
try {
final arquivo = await _pegarArquivo();
return arquivo.readAsStringSync();
} catch (e) {
return 'erro';
}
}
do nothing:
_lerArquivo() async {
final arquivo = await _pegarArquivo();
return arquivo.readAsStringSync();
}
The good practice
To avoid future errors (i.e. transform run-time errors into compile-time errors), declare your variable types, return types and type parameters explicitely:
String _lerArquivo() async {
try {
final arquivo = await _pegarArquivo();
return arquivo.readAsStringSync();
} catch (e) {
// ...
}
}
Related
Probably the contacts have duplicates, but I want the duplicates to be accepted. The basic idea is to access the contact list and populate the values to a dropdownMenu and let the user to select a contact from there and save to a file. I have already initialised the dropdownMenu with a string "Select a contact" through a variable.
Exception has occurred.
_AssertionError ('package:flutter/src/material/dropdown.dart': Failed assertion: line 890 pos 15: 'items == null || items.isEmpty || value == null ||
items.where((DropdownMenuItem<T> item) {
return item.value == value;
}).length == 1': There should be exactly one item with [DropdownButton]'s value: Select a contact.
Either zero or 2 or more [DropdownMenuItem]s were detected with the same value)
Here is the complete code
import 'package:flutter/material.dart';
import 'package:contacts_service/contacts_service.dart';
import 'package:permission_handler/permission_handler.dart';
import 'dart:io';
import 'dart:convert';
import 'package:url_launcher/url_launcher.dart';
import 'package:path_provider/path_provider.dart';
class Interface extends StatelessWidget {
const Interface({super.key});
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('pAM'),
),
body: const ContactSelector(),
);
}
}
class ContactSelector extends StatefulWidget {
const ContactSelector({super.key});
#override
_ContactSelectorState createState() => _ContactSelectorState();
}
class _ContactSelectorState extends State<ContactSelector> {
Contact _selectedContact = Contact();
late bool _isTrue;
late Iterable<Contact> _contacts;
List<DropdownMenuItem<String>> _dropdownItems = [];
String _selectedName = "Select Contact";
//late List<DropdownMenuItem<String>> _dropdownItems;
#override
void initState() {
super.initState();
_getContacts();
_selectedName = _dropdownItems.isNotEmpty
? _dropdownItems[0].value!
: 'Select a contact';
}
#override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
if (_dropdownItems != null)
DropdownButton<String>(
value: _selectedName,
items: _dropdownItems,
onChanged: (newValue) {
_onContactChanged(newValue!);
},
)
else
const Text("Loading...")
],
);
}
String? encodeQueryParameters(Map<String, String> params) {
return params.entries
.map((e) =>
'${Uri.encodeComponent(e.key)}=${Uri.encodeComponent(e.value)}')
.join('&');
}
void _sendMessage(String message) async {
String phoneNumber = _selectedContact.phones.toString();
Uri uri = Uri(
scheme: 'sms',
path: phoneNumber,
query: encodeQueryParameters(<String, String>{
'body': 'Welcome to pAM',
}),
);
if (await canLaunchUrl(uri)) {
await canLaunchUrl(uri);
} else {
throw 'Could not send SMS';
}
}
_getContacts() async {
_contacts = await ContactsService.getContacts(withThumbnails: false);
_dropdownItems = _contacts
.map((c) => DropdownMenuItem(
value: c.displayName,
child: Text(c.displayName.toString()),
))
.toList();
setState(() {});
}
_onContactChanged(String newValue) {
setState(() {
_selectedName = newValue;
_selectedContact =
_contacts.firstWhere((c) => c.displayName == _selectedName);
});
_saveContactToFile(_selectedContact);
_readJson();
}
_saveContactToFile(Contact contact) async {
final directory = await getApplicationDocumentsDirectory();
final file = File('${directory.path}/selected_contact.txt');
if (!(await file.exists())) {
file.create();
}
file.writeAsString(jsonEncode(contact.toMap()));
}
void _readJson() async {
final directory = await getApplicationDocumentsDirectory();
final file = File('${directory.path}/true.json');
if (await file.exists()) {
final content = jsonDecode(await file.readAsString());
if (content["isTrue"]) {
_promptMessage();
} else {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: const Text('Reminder'),
content: const Text(
"You can continue your work, remember your loved ones misses you"),
actions: <Widget>[
ElevatedButton(
child: const Text('OK'),
onPressed: () {
Navigator.of(context).pop();
},
),
],
);
});
}
}
}
_promptMessage() {
if (_isTrue) {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: const Text('Select a message'),
content: SingleChildScrollView(
child: ListBody(
children: <Widget>[
InkWell(
child: const Text('How are you?'),
onTap: () {
_sendMessage('How are you?');
}),
InkWell(
child: const Text('What are you up to?'),
onTap: () {
_sendMessage('What are you up to?');
}),
InkWell(
child: const Text('What is for dinner?'),
onTap: () {
_sendMessage('What is for dinner?');
}),
],
),
),
actions: <Widget>[
ElevatedButton(
child: const Text('Cancel'),
onPressed: () {
Navigator.of(context).pop();
},
),
],
);
},
);
}
}
}
This is the key part of the error message:
There should be exactly one item with [DropdownButton]'s value: Select a contact.
You are setting the value of the DropdownButton to "Select a contact" (presumably because _dropdownItems.isNotEmpty == false), but none of the DropdownMenuItems that you have given to the DropdownButton via its items property has "Select a contact" as its value. You might want to look into the use of the hint property to show the "Select a contact", well, hint.
Something like the (untested) code below:
DropdownButton<String>(
hint: Text("Select a contact")
value: _dropdownItems.isNotEmpty ? _dropdownItems.first.value : null,
items: _dropdownItems,
onChanged: (newValue) {
_onContactChanged(newValue!);
},
)
Error is:
The body might complete normally, causing 'null' to be returned, but the return type, 'FutureOr<List<Map<dynamic, dynamic>>>', is a potentially non-nullable type.
Try adding either a return or a throw statement at the end.
I tried adding an else function, yet I don't know where to add it exactly. Not to mention, that I don't know whether this is the best way to solve this error or not.
This is my code:
import 'package:algorithm_learn/sqldb.dart';
import 'package:flutter/material.dart';
class Home extends StatefulWidget {
const Home({super.key});
#override
State<Home> createState() => _HomeState();
}
class _HomeState extends State<Home> {
SqlDb sqlDb = SqlDb();
bool isloading = true;
List coing = [];
Future<List<Map>> readData() async {
List<Map> response = await sqlDb.readData("SELECT * FROM 'Coing'"); //(ERROR IS HERE)
coing.addAll(response);
isloading = false;
if (this.mounted) {
setState(() {});
}
}
#override
void initState() {
readData();
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('HomePage'),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
Navigator.of(context).pushNamed("addperson");
},
child: const Icon(Icons.add),
),
body: isloading == true ?
Center(child: Text("Loading..."))
: Container(
child: ListView(
children: [
MaterialButton(
onPressed: () async {
await sqlDb.mydeleteDatabase();
},
child: const Text("Delete Database"),
),
ListView.builder(
itemCount: coing.length,
physics: NeverScrollableScrollPhysics(),
shrinkWrap: true,
itemBuilder: (context, i) {
return Card(
child: ListTile(
title: Text("${coing[i]['first_name']}"),
subtitle: Text("${coing[i]['last_name']}"),
trailing: IconButton(
onPressed: () async {
int response = await sqlDb.deleteData(
"DELETE FROM coing WHERE id = ${coing[i]['id']}");
if (response > 0) {
coing.removeWhere(
(element) => element["id"] == coing[i]['id']);
setState(() {});
}
},
icon: Icon(Icons.delete, color: Colors.red),
),
),
);
},
),
],
),
),
);
}
}
You can mark it as nullable by adding a ?
Future<List<Map>> readData() async {
List<Map>? response = await sqlDb.readData("SELECT * FROM 'Coing'");
if(response != null)
{
coing.addAll(response);
}
isloading = false;
if (this.mounted) {
setState(() {});
}
}
bloc.dart
part 'news_article_loader_event.dart';
part 'news_article_loader_state.dart';
part 'news_article_loader_bloc.freezed.dart';
#injectable
class NewsArticleLoaderBloc
extends Bloc<NewsArticleLoaderEvent, NewsArticleLoaderState> {
NewsArticleLoaderBloc(this.iBlogPost)
: super(NewsArticleLoaderState.initial());
final INewsArticle iBlogPost;
#override
Stream<NewsArticleLoaderState> mapEventToState(
NewsArticleLoaderEvent event) async* {
yield NewsArticleLoaderState.loadInProgress();
final failureOrSuccess = await iBlogPost.getDataNews();
yield failureOrSuccess.fold((l) => NewsArticleLoaderState.loadFailure(l),
(r) => NewsArticleLoaderState.loadSuccess(r));
}
}
repository.dart
import 'dart:convert';
import 'package:dartz/dartz.dart';
import 'package:dio/dio.dart';
import 'package:injectable/injectable.dart';
import 'package:mone/app_constant.dart';
import 'package:mone/model/news/blog_post.dart';
import 'package:mone/repositories/i_news_repository.dart';
import 'package:shared_preferences/shared_preferences.dart';
#Injectable(as: INewsArticle)
class BLogPostRepository implements INewsArticle {
final Dio dio;
// int page = 1;
final SharedPreferences prefs;
BLogPostRepository(this.dio, this.prefs);
#override
Future<Either<String, List<NewsBlogModel>>> getDataNews() async {
try {
final token = prefs.get(kAccesTokenKey);
String url = '/api/v1/cms?size=30';
var response = await dio.get(url,
options: Options(
headers: {
'Accept': 'aplication/json',
'Authorization': 'bearer $token',
},
));
if (response.statusCode == 200) {
final listNews = (response.data['items'] as List)
.map((e) => NewsBlogModel.fromJson(e as Map<String, dynamic>))
.toList();
return right(listNews);
}
return left('error: ${response.statusCode}');
} catch (e) {
return left('error get data $e');
}
}
#override
Future<Either<String, List<NewsBlogModel>>> getDataNewsbyId() async {
// TODO: implement getDataNewsbyId
try {
final token = prefs.get(kAccesTokenKey);
String urlbyid = '/api/v1/cms/id';
var response = await dio.get(urlbyid,
options: Options(headers: {
'Accept': 'aplication/json',
'Authorization': 'bearer $token',
}));
if (response.statusCode == 200) {
final dataNewsbyId = (response.data['items'] as List)
.map((e) => NewsBlogModel.fromJson(e as Map<String, dynamic>))
.toList();
return right(dataNewsbyId);
}
return left("error:${response.statusCode}");
} catch (e) {
return left("error get data $e");
}
}
}
I have the code below where I have provided the BLoC code for the code and also for the repository code. but I'm still confused about how to connect it to the UI that I made. explanation please.
below also my code has succeeded to call all data list from API. but when I want to try to find the data list in the get, but can't.
UI.dart
part of '../screens.dart';
class NewsScreen extends StatefulWidget {
#override
_NewsScreenState createState() => _NewsScreenState();
}
class _NewsScreenState extends State<NewsScreen> {
Widget customTitleBar = const Text('Berita');
Icon customIcon = new Icon(Icons.search, color: primaryColor);
TextEditingController filterController = TextEditingController();
// String filter = '';
// void initState(){
// }
#override
Widget build(BuildContext context) {
// bloc provider berguna untuk menambahkan data dari bloc ke ui,
return BlocProvider(
create: (context) =>
getIt<NewsArticleLoaderBloc>()..add(NewsArticleLoaderEvent.started()),
child: Scaffold(
appBar: AppBar(
title: customTitleBar,
actions: <Widget>[
IconButton(
onPressed: () {
setState(() {
if (this.customIcon.icon == Icons.search) {
this.customIcon =
new Icon(Icons.close, color: primaryColor, size: 30);
this.customTitleBar = new TextField(
style: new TextStyle(
color: Colors.white,
),
decoration: new InputDecoration(
prefixIcon: new Icon(Icons.search,
color: primaryColor, size: 30),
hintText: "Search...",
hintStyle: new TextStyle(color: Colors.white)),
cursorColor: primaryColor,
onChanged: (value) async {
if (value.isEmpty) {
setState(() {});
return;
}
},
controller:filterController,
);
} else {
this.customIcon =
new Icon(Icons.search, color: primaryColor, size: 30);
this.customTitleBar = new Text("Berita");
}
});
},
icon: customIcon,
),
],
),
body: BlocBuilder<NewsArticleLoaderBloc, NewsArticleLoaderState>(
builder: (context, state) {
return state.map(
initial: (_) => Container(),
loadInProgress: (_) => Center(
child: SpinKitCircle(
color: primaryColor,
),
),
loadFailure: (state) => Center(
child: Text(state.failure),
),
loadSuccess: (state) {
if (state.datenews.isEmpty) {
return Center(
child: Text("data tidak di temukan"),
);
} else {
return ListView.separated(
padding: EdgeInsets.all(8.0),
itemCount: state.datenews.length,
separatorBuilder: (BuildContext context, int index) {
return SizedBox(
height: 4,
);
},
itemBuilder: (BuildContext context, int index) {
final newsBlog = state.datenews[index];
return ContentNews(
newsBlog: newsBlog,
);
},
);
}
},
);
},
),
),
);
}
}
I was create SharedPreferences to save user loading in logon page. Then data of user will be save in SharedPreferences and move to main page. But my problem now in main page I need use this variable in different places in main page. But I cant do that.
I need to make variable of logindata can use in each places in main page I try to use in drawer to make logout. No I get error as:
Undefined name 'logindata'.
this is my code:
void initial() async {
logindata = await SharedPreferences.getInstance();
setState(() {
username = logindata.getString('username');
return username;
});
}
my full code:
import 'package:flutter/material.dart';
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:shared_preferences/shared_preferences.dart';
import 'addnewtopics.dart';
import 'DetilesOfMainPage.dart';
import 'loginpage.dart';
class MyApp extends StatelessWidget {
final String email;
MyApp({Key key, #required this.email}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('JSON ListView')
),
drawer: Drawer(
child: ListView(
padding: EdgeInsets.zero,
children: <Widget>[
DrawerHeader(
child: Text('Drawer Header'),
decoration: BoxDecoration(
color: Colors.blue,
),
),
ListTile(
title: Text('Item 1'),
onTap: () {
logindata.setBool('login', true);// here I need to use It ========================
Navigator.pushReplacement(context,
new MaterialPageRoute(builder: (context) => LoginUser()));
Navigator.pop(context);
},
),
ListTile(
title: Text('Item 2'),
onTap: () {
// Navigator.pop(context);
},
),
],
),
),
body: JsonImageList(),
floatingActionButton: FloatingActionButton(
onPressed: () {
Navigator.push(context, MaterialPageRoute(builder: (context) => UploadImageDemo()
),);
},
child: Icon(Icons.add),
),
));
}
}
class Flowerdata {
int id;
String flowerName;
String flowerImageURL;
Flowerdata({
this.id,
this.flowerName,
this.flowerImageURL
});
factory Flowerdata.fromJson(Map<String, dynamic> json) {
return Flowerdata(
id: json['id'],
flowerName: json['nametopics'],
flowerImageURL: json['image']
);
}
}
class JsonImageList extends StatefulWidget {
JsonImageListWidget createState() => JsonImageListWidget();
}
class JsonImageListWidget extends State {
SharedPreferences logindata;
String username;
#override
void initState() {
// TODO: implement initState
super.initState();
initial();
}
void initial() async {
logindata = await SharedPreferences.getInstance();
setState(() {
username = logindata.getString('username');
return username;
});
}
final String apiURL = 'http://xxxxxxxxx/getFlowersList.php';
Future<List<Flowerdata>> fetchFlowers() async {
var response = await http.get(apiURL);
if (response.statusCode == 200) {
final items = json.decode(response.body).cast<Map<String, dynamic>>();
List<Flowerdata> listOfFruits = items.map<Flowerdata>((json) {
return Flowerdata.fromJson(json);
}).toList();
return listOfFruits;
}
else {
throw Exception('Failed to load data from Server.');
}
}
getItemAndNavigate(String item, BuildContext context){
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SecondScreen(itemHolder : item)
)
);
}
#override
Widget build(BuildContext context) {
return FutureBuilder<List<Flowerdata>>(
future: fetchFlowers(),
builder: (context, snapshot) {
if (!snapshot.hasData) return Center(
child: CircularProgressIndicator()
);
return ListView(
children: snapshot.data
.map((data) => Column(children: <Widget>[
GestureDetector(
onTap: ()=>{
getItemAndNavigate(data.flowerName, context)
},
child: Row(
children: [
Container(
width: 200,
height: 100,
margin: EdgeInsets.fromLTRB(10, 0, 10, 0),
child: ClipRRect(
borderRadius: BorderRadius.circular(8.0),
child:
Image.network(data.flowerImageURL,
width: 200, height: 100, fit: BoxFit.cover,))),
Flexible(child:
Text(data.flowerName,
style: TextStyle(fontSize: 18)))
]),),
Divider(color: Colors.black),
],))
.toList(),
);
},
);
}
}
Anyone know how can make that?
You need var keyword, in your case you can directly use
var logindata = await SharedPreferences.getInstance();
You do not need to make it global, because SharedPreferences.getInstance() is Singleton
Every time you use var logindata = await SharedPreferences.getInstance(); will get the same instance
Also there is no performance issue when you call getInstance(), because it's cached, you can see source code snippet below
class SharedPreferences {
SharedPreferences._(this._preferenceCache);
...
static Future<SharedPreferences> getInstance() async {
if (_completer == null) {
_completer = Completer<SharedPreferences>();
try {
final Map<String, Object> preferencesMap =
await _getSharedPreferencesMap();
_completer.complete(SharedPreferences._(preferencesMap));
} on Exception catch (e) {
// If there's an error, explicitly return the future with an error.
// then set the completer to null so we can retry.
_completer.completeError(e);
final Future<SharedPreferences> sharedPrefsFuture = _completer.future;
_completer = null;
return sharedPrefsFuture;
}
}
return _completer.future;
When you declare a String outside of class and does not contain _ before variable name like _localString it become global
String globalString = ""; //global, import can be seen
String _localString = ""; //local and can only be seen in this file, import can not seen
void main() async{
var logindata = await SharedPreferences.getInstance();
runApp(MyApp());
}
You simply need to put your variable outside of any class or method. An example is to create a file globals.dart then put all your globals in it and import the file when you need.
Example
// globals.dart
String globalString;
int globalInt;
bool globalBool;
// in any other file
import 'globals.dart' as globals;
globals.globalString = "Global String";
I am trying to fetch data from the API and I am able to get logs but setState is not working.
Overall what I want to achieve is if there is response show the data on the screen, if there is any error in the API or on server or anything else I want to show it in the snackbar. My moto is to show errors as well.
Below is my model class
import 'http.dart';
class User {
int userId;
int id;
String title;
String body;
User({this.userId, this.id, this.title, this.body});
User.fromJson(Map<String, dynamic> json) {
userId = json['userId'];
id = json['id'];
title = json['title'];
body = json['body'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['userId'] = this.userId;
data['id'] = this.id;
data['title'] = this.title;
data['body'] = this.body;
return data;
}
}
class UserExt {
static getUserInfo(Function(User user) success, Function(String errorMesssage) error) async{
final response = await HTTP.get(api: "https://jsonplaceholder.typicode.com/posts/1");
if(response.isSuccess == true) {
success(User.fromJson(response.response));
} else {
error(response.response);
}
}
}
Below is my http.dart file
import 'dart:html';
import 'package:flutter/cupertino.dart';
import 'package:http/http.dart' as http;
import 'dart:convert' as convert;
import 'package:http/http.dart';
const _timeoutDuration = Duration(seconds: 5);
class HTTP {
static Future<HttpResponse> get({#required String api}) async {
try {
Response response = await http.get(api).timeout(_timeoutDuration);
return _modeledResponse(response);
} catch (error) {
return HttpResponse(isSuccess: false, response: error.toString());
}
}
static Future<HttpResponse> _modeledResponse(Response response) async {
try {
if(response.statusCode == HttpStatus.ok) {
var jsonResponse = convert.jsonDecode(response.body);
return HttpResponse(isSuccess: true, response: jsonResponse);
} else {
return HttpResponse(isSuccess: false, response: response.statusCode.toString());
}
} catch (error) {
return HttpResponse(isSuccess: false, response: error.toString());
}
}
}
class HttpResponse {
final bool isSuccess;
final dynamic response;
HttpResponse({#required this.isSuccess, #required this.response});
}
Below is my screen from where I am calling the API.
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:http_request/User.dart';
import 'http.dart';
class ApiCalling extends StatefulWidget {
#override
_ApiCallingState createState() => _ApiCallingState();
}
class _ApiCallingState extends State<ApiCalling> {
bool showLoader = false;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Stack(
children: <Widget>[
Center(
child: RaisedButton(
child: Text("Call API"),
onPressed: () {
setState(() {
showLoader = true;
});
UserExt.getUserInfo((user){
print("UUUser id = ${user.userId}");
Scaffold.of(context).showSnackBar(SnackBar(content: Text("${user.userId}"),));
setState(() {
showLoader = false;
});
}, (error){
Scaffold.of(context).showSnackBar(SnackBar(content: Text("${error}"),));
setState(() {
showLoader = false;
});
});
},
),
),
Visibility(child: CircularProgressIndicator(backgroundColor: Colors.pink,), visible: showLoader,),
],
),
),
);
}
}
In the current code indicator is not getting show/hide or snackbar is also not getting displayed.
Just made some changes and addons just check the below code :
import 'package:flutter/material.dart';
import 'package:sample_project_for_api/model.dart';
void main() => runApp(ApiCalling());
class ApiCalling extends StatefulWidget {
#override
_ApiCallingState createState() => _ApiCallingState();
}
class _ApiCallingState extends State<ApiCalling> {
bool showLoader = false;
final _scaffoldKey = GlobalKey<ScaffoldState>();
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
key: _scaffoldKey,
body: Center(
child: Stack(
children: <Widget>[
Builder(
builder: (context) {
return Column(
children: <Widget>[
RaisedButton(
child: Text(
"this is first api call under the builder widget"),
onPressed: () {
UserExt.getUserInfo((user) {
print("UUUser id = ${user.userId}");
Scaffold.of(context).showSnackBar(SnackBar(
backgroundColor: Colors.redAccent,
content:
Text("This is you user id ${user.userId}"),
));
}, (error) {
Scaffold.of(context).showSnackBar(SnackBar(
duration: Duration(seconds: 2),
backgroundColor: Colors.redAccent,
content: Text("${error.toString()}"),
));
});
},
),
RaisedButton(
child: Text(
"this is second api call under the builder widget"),
onPressed: () {
UserExt.getUserInfo((user) {
print("UUUser id = ${user.userId}");
Scaffold.of(context).showSnackBar(SnackBar(
backgroundColor: Colors.redAccent,
content:
Text("This is you user id ${user.userId}"),
));
}, (error) {
Scaffold.of(context).showSnackBar(SnackBar(
duration: Duration(seconds: 2),
backgroundColor: Colors.redAccent,
content: Text("${error.toString()}"),
));
});
},
)
],
);
},
),
Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
RaisedButton(
child: Text("call snacker using the global key"),
onPressed: () {
setState(() {
showLoader = true;
});
UserExt.getUserInfo((user) {
print("UUUser id = ${user.userId}");
_scaffoldKey.currentState.showSnackBar(new SnackBar(
duration: Duration(seconds: 2),
content: new Text(
"This is you user id :${user.userId}")));
setState(() {
showLoader = false;
});
}, (error) {
_scaffoldKey.currentState.showSnackBar(new SnackBar(
duration: Duration(seconds: 2),
content: new Text("${error.toString()}")));
setState(() {
showLoader = false;
});
});
},
),
Secondbutton(),
],
),
),
Visibility(
child: CircularProgressIndicator(
backgroundColor: Colors.pink,
),
visible: showLoader,
),
],
),
),
),
);
}
}
class Secondbutton extends StatefulWidget {
#override
_SecondbuttonState createState() => _SecondbuttonState();
}
class _SecondbuttonState extends State<Secondbutton> {
#override
Widget build(BuildContext context) {
return RaisedButton(
child: Text("calling snacker without using the global key"),
onPressed: () {
UserExt.getUserInfo((user) {
print("UUUser id = ${user.userId}");
Scaffold.of(context).showSnackBar(SnackBar(
backgroundColor: Colors.redAccent,
content: Text("This is you user id ${user.userId}"),
));
}, (error) {
Scaffold.of(context).showSnackBar(SnackBar(
duration: Duration(seconds: 2),
backgroundColor: Colors.redAccent,
content: Text("${error.toString()}"),
));
});
},
);
}
}
Your problem was because it was not getting the proper context.
From the official Documentation of flutter https://api.flutter.dev/flutter/material/Scaffold/of.html
When the Scaffold is actually created in the same build function, the context argument to the build function can't be used to find the Scaffold (since it's "above" the widget being returned in the widget tree). In such cases, the following technique with a Builder can be used to provide a new scope with a BuildContext that is "under" the Scaffold:
So basically the problem is with your context of Scaffold,so instead of using context of Direct parent that instantiate the Scaffold, use the context of the child.
Below code will work.
class _ApiCallingState extends State<ApiCalling> {
bool showLoader = false;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Builder(
builder: (context)=>
Center(
child: Stack(
children: <Widget>[
Center(
child: RaisedButton(
child: Text("Call API"),
onPressed: () {
setState(() {
showLoader = true;
});
UserExt.getUserInfo((user) {
print("UUUser id = ${user.userId}");
print("context==$context");
Scaffold.of(context).showSnackBar(SnackBar(
content: Text(" User Id${user.userId}"),
));
setState(() {
showLoader = false;
});
}, (error) {
setState(() {
showLoader = false;
});
Scaffold.of(context).showSnackBar(SnackBar(
content: Text("${error}"),
));
});
},
),
),
Visibility(
child:
Center(
child:CircularProgressIndicator(
backgroundColor: Colors.pink,
),
),
visible: showLoader,
)
],
),
),
)
);
}
}
declare a global key
final _scaffoldKey = GlobalKey();
and in UI
_scaffoldKey.currentState.showSnackBar(snackbar);