Flutter: How to show loading screen after the url is added? - android

I'm building a URL shortner app. I want to show a loading screen after the url is entered. This is my code. I'm a beginner to flutter. Please help me since this is my first app. The code is given below. As you can see the I'm using FutureBuilder so if the url list is empty it shows a corresponding message but I want it to disappear after the ok button of the alertdialog is pressed.
class _homePageState extends State<homePage> {
List userURL = List();
List item = List();
Future<List> getdata() async {
//JSON Parser
var url = 'https://api.shrtco.de/v2/shorten?url=${userURL.last}';
var respons = await http.get(url);
var result = jsonDecode(respons.body);
item.add(result['result']['short_link']); //dictionary parse
print(item);
return item;
}
createAlertDialog(BuildContext context) {
//method for alertdialog
//promise to return string
TextEditingController customController =
TextEditingController(); //new texteditingc object
return showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: Text("Enter URL: "),
content: TextField(
controller: customController,
),
actions: [
MaterialButton(
elevation: 5.0,
child: Text("OK"),
onPressed: () {
if (customController.text != null &&
customController.text != "") {
userURL.add(customController.text);
}
setState(() {});
Navigator.of(context).pop();
},
)
],
);
});
}
#override
Widget build(BuildContext context) {
String temp;
return Scaffold(
appBar: AppBar(
title: Text("Shortie"),
),
body: Padding(
padding: const EdgeInsets.all(8.0),
child: FutureBuilder(
future: getdata(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.data == null) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.sentiment_very_dissatisfied,
color: Colors.grey,
size: 80,
),
Text(
"No short links to display",
style: TextStyle(
color: Colors.grey[700],
fontSize: 15,
//fontWeight: FontWeight.bold
),
),
]));
} else {
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (context, index) {
return ListTile(
leading: Icon(Icons.link),
title: Text(snapshot.data[index]),
subtitle: Text(userURL[index]),
onTap: () {
Share.share(
'Check out the short link I just shared with the application Shortie: ${snapshot.data[index]}',
subject: 'Shortie short link');
print(snapshot.data[index]);
},
);
},
);
}
},
)),
floatingActionButton: FloatingActionButton(
onPressed: () {
createAlertDialog(context).then((onValue) {
temp = onValue;
print(temp);
});

You can make use of the connection state class of the FutureBuilder as follows:
FutureBuilder(
future: getdata(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return CircularProgressIndicator(backgroundColor: Colors.blue);
} else {
return Container();
}
},
);
Also, on your button you need to call setState() to trigger the view reload, which in turn will again check the connectionState, if the async function is still in progress you will see the loading indicator, otherwise something you put in else

Related

how can i fix this error of my todo list?

I have made an to do list with firebase. but when i click to create a new to do, i can't see anything apear on my page but in firebase it does show the string.
How can i fix this
(this is in flutter)
logcat:
2022-10-19 15:24:50.758 23369-23584 flutter com.example.voorbeeld I apen created
import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
class video_info extends StatefulWidget {
#override
_video_infoState createState() => _video_infoState();
}
class _video_infoState extends State<video_info> {
String todoTitle = "";
createTodos() {
DocumentReference documentReference =
FirebaseFirestore.instance.collection("MyTodos").doc(todoTitle);
//Map
Map<String, String> todos = {"todoTitle": todoTitle};
documentReference.set(todos).whenComplete(() {
print("$todoTitle created");
});
}
deleteTodos(item) {
DocumentReference documentReference =
FirebaseFirestore.instance.collection("MyTodos").doc(item);
documentReference.delete().whenComplete(() {
print("$item deleted");
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("mytodos"),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8)),
title: Text("Add Todolist"),
content: TextField(
onChanged: (String value) {
todoTitle = value;
},
),
actions: <Widget>[
TextButton(
onPressed:() {
createTodos();
Navigator.of(context).pop();
},
child: Text("Add"))
],
);
});
},
child: Icon(
Icons.add,
color: Colors.white,
),
),
body: StreamBuilder(
stream: FirebaseFirestore.instance.collection("Mytodos").snapshots(),
builder: (context, snapshots) {
if (snapshots.hasData) {
return ListView.builder(
shrinkWrap: true,
itemCount: snapshots.data?.docs.length,
itemBuilder: (context, index) {
DocumentSnapshot documentSnapshot =
snapshots.data!.docs[index];
return Dismissible(
onDismissed: (direction) {
deleteTodos(documentSnapshot["todoTitle"]);
},
key: Key(documentSnapshot["todoTitle"]),
child: Card(
elevation: 4,
margin: EdgeInsets.all(8),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8)),
child: ListTile(
title: Text(documentSnapshot["todoTitle"]),
trailing: IconButton(
icon: Icon(
Icons.delete,
color: Colors.red,
),
onPressed: () {
deleteTodos(documentSnapshot["todoTitle"]);
}),
),
));
});
} else {
return Align(
alignment: FractionalOffset.bottomCenter,
child: CircularProgressIndicator(),
);
}
}),
);
}}
also does anyone know a link to an tuturial where they explain how i can link the database to a user login.
You're using another collection.
You are adding your todo to this collection:
FirebaseFirestore.instance.collection("MyTodos")
But in your StreamBuilder you use the collection "Mytodos":
stream: FirebaseFirestore.instance.collection("Mytodos").snapshots(),
Try creating a stream variable on state class
late final myStream = FirebaseFirestore.instance.collection("MyTodos").snapshots();
#override
Widget build(BuildContext context) {
....
body: StreamBuilder(
stream: myStream

How to pop only the circular progress indicator in flutter?

'''buildShowDialog(BuildContext context) {
return showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext cont1) {
return Center(
child: LoadingAnimationWidget.threeArchedCircle(
color: Colors.white, size: 50),
);
});
}
loginFunction(http.Client client, jsonBody) async {
buildShowDialog(context);
var url = Uri.parse("https://my-office-timesheet.herokuapp.com/api/signin");
var respsonse = await client.post(url, body: jsonBody);
if (respsonse.statusCode == 200) {
Navigator.of(context).pop();
if (this.mounted) {
setState(() {
jsonResponse = Post.fromJson(json.decode(respsonse.body));
});
}
if (jsonResponse!.success == true) {
if (this.mounted) {
Navigator.of(context).pushAndRemoveUntil(
MaterialPageRoute(builder: (BuildContext context) => Dashboard()),
(Route<dynamic> route) => false);
//this code is to route without a back button.
}
} else {
showDialog(
context: context,
builder: (ctx) => AlertDialog(
title: Text("Message:"),
content: Text("${jsonResponse?.message}"),
actions: [
ElevatedButton(
onPressed: () {
Navigator.of(ctx).pop();
if (this.mounted) {
setState(() {
jsonResponse = null;
});
}
},
child: const Text("Ok"),
),
],
),
);
}
}
return respsonse;
}'''
List view builder
ListView.builder(
physics: const NeverScrollableScrollPhysics(),
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemCount: _fieldNames.length,
itemBuilder: (context, index) {
return Column(
children: [
FieldBuilderForm(
key: ValueKey(index),
fieldColor: _fieldColor[index],
fieldTextColor: _fieldTextColor[index],
fieldName: _fieldNames[index],
controller: controller[index],
size: displaySize,
),
In the above code when I pop the LoadingAnimationWidget. The list view builder which I made is the one popping instead. Any suggestions?

Getting Album Artwork in flutter

I am trying to create a music app. I managed to get all the songs list but I cannot seem to get album artwork from the song metadata. I am using flutter_audio_query: ^0.3.5+6 plugin for this.
I cannot get the artwork by using songs[index].albumArtwork. It always returns null instead of the image path. What is the problem with my code?
Here is my code
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:flutter_audio_query/flutter_audio_query.dart';
class HomePage extends StatefulWidget {
const HomePage({Key key}) : super(key: key);
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
final FlutterAudioQuery audioQuery = FlutterAudioQuery();
List<SongInfo> songs = [];
#override
void initState() {
super.initState();
checkPermission();
getAllSongs();
}
Future<void> getAllSongs() async {
songs = await audioQuery.getSongs();
}
Future<void> checkPermission() async {
if (await Permission.storage.request().isGranted) {
setState(() {});
} else {
_showDialog();
}
}
void _showDialog() async {
return showDialog<void>(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return AlertDialog(
title: const Text('Warning'),
content: SingleChildScrollView(
child: ListBody(
children: const <Widget>[
Text('The app needs storage permission in order to work'),
],
),
),
actions: <Widget>[
TextButton(
child: const Text('OK'),
onPressed: () async {
Navigator.of(context).pop();
await checkPermission();
},
),
],
);
},
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Color(0xFF3C3C3C),
appBar: AppBar(
elevation: 0.0,
centerTitle: true,
toolbarHeight: 64.0,
leading: Icon(
Icons.music_note_rounded,
),
actions: [
IconButton(
onPressed: () {},
icon: Icon(Icons.search_rounded),
),
],
backgroundColor: Colors.transparent,
title: Text(
"All Songs",
style: GoogleFonts.expletusSans(
fontWeight: FontWeight.bold,
),
),
),
body: ListView.builder(
itemCount: songs.length,
itemBuilder: (context, index) {
return ListTile(
leading: Image.asset(
songs[index].albumArtwork != null
? songs[index].albumArtwork
: "assets/placeholder.png",
),
title: Text(songs[index].title),
subtitle: Text(songs[index].artist),
);
},
),
);
}
}
Sometimes albumArtwork will return a null value. In this case you need to use [FlutterAudioQuery().getArtwork()].
Documentation
Use ResourceType.ALBUM to get album image and ResourceType.SONG to song image.
Example:
// check if artistArtPath isn't available.
(song.artwork == null)
? FutureBuilder<Uint8List>(
future: audioQuery.getArtwork(
type: ResourceType.SONG, id: song.id),
builder: (_, snapshot) {
if (snapshot.data == null)
return Container(
height: 250.0,
child: Center(
child: CircularProgressIndicator(),
),
);
return CardItemWidget(
height: 250.0,
title: artist.name,
// The image bytes
// You can use Image.memory widget constructor
// or MemoryImage image provider class to load image from bytes
// or a different approach.
rawImage: snapshot.data,
);
}) :
// or you can load image from File path if available.
Image.file( File( artist.artistArtPath ) )

Exception caught by widgets library - Flutter

As I mentioned at the title, I got this error:
Exception caught by widgets library
Closure call with mismatched arguments: function '[]'
Receiver: Closure: () => Map<String, dynamic> from Function 'data':.
Tried calling: []("imageURL")
Found: []() => Map<String, dynamic>
I have been trying to use it to get data from firestore and show it on my app page. But I can't get the data from collection, especially for images. I referenced this tutorial from youtube. Even though I've done everything same but I couldn't handle it. Maybe bc of version. I'd be glad if you help me.
class _HomeState extends State<Home> {
PostService postService = new PostService();
Stream postStream;
//Stream postsStream;
Widget postsList() {
return SingleChildScrollView(
child: postStream != null
? Column(
children: <Widget>[
StreamBuilder(
//stream: postStream,
stream: postStream,
builder: (context, snapshot)
{
if(snapshot.data == null) return CircularProgressIndicator();
return ListView.builder(
padding: EdgeInsets.symmetric(horizontal:16.0),
itemCount: snapshot.data.docs.length,
shrinkWrap: true,
itemBuilder: (context, index) {
return PostTile(
imgUrl: snapshot.data.docs[index].data['imageURL'],
title: snapshot.data.docs[index].data['postTitle'],
desc: snapshot.data.docs[index].data['postDesc'],
city: snapshot.data.docs[index].data['cityName'],
);
});
}),
],
): Container(
alignment: Alignment.center,
child: CircularProgressIndicator(),
),
);
}
#override
void initState() {
postService.getPostData().then((result) {
setState(() {
postStream = result;
});
});
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
title: Text('Ana Sayfa'),
backgroundColor: Colors.amber,
elevation: 0.0,
actions: <Widget>[
FlatButton.icon(
icon: Icon(Icons.group_rounded),
label: Text(''),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => KullaniciSayfasi()));
},
),
],
),
body: postsList(),
floatingActionButton: Container(
padding: EdgeInsets.symmetric(vertical: 10.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
FloatingActionButton(
onPressed: () {
//Ekleme butonuna basıldığında
Navigator.push(context,
MaterialPageRoute(builder: (context) => CreatePost()));
},
child: Icon(Icons.add),
)
],
),
),
);
}
}
Code for post service
import 'package:cloud_firestore/cloud_firestore.dart';
class PostService{
Future<void> addData(postData) async{
FirebaseFirestore.instance.collection("posts").add(postData).catchError((e){
print(e);
});
}
getPostData() async{
return await FirebaseFirestore.instance.collection("posts").snapshots();
}
}
There was a breaking change on firebase plugins and many things have changed. E.g i see you're doing snapshot.data.docs[index].data['imageURL'] this has been changed to snapshot.data.docs[index].data()['imageURL']. Kindly check the docs for the updated API refrences

Flutter: Adding text from alert dialog to list view

I'm trying to add user input from the alert dialog to list view. Every time I run it, the alert dialog accepts input and the item list gets updated but the list view wont update. The state of the app wont change after I press OK on the alert dialog button. Please help me with this issue as I'm new to flutter.
Future<String> createAlertDialog(BuildContext context) {
//promise to return string
TextEditingController customController =
TextEditingController(); //new texteditingc object
return showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: Text("Enter URL: "),
content: TextField(
controller: customController,
),
actions: [
MaterialButton(
elevation: 5.0,
child: Text("OK"),
onPressed: () {
Navigator.of(context).pop(customController.text.toString());
},
)
],
);
});
}
#override
Widget build(BuildContext context) {
List item = List();
item=['HI'];
String temp;
return Scaffold(
appBar: AppBar(
title: Text("Shortie"),
),
body: Padding(
padding: const EdgeInsets.all(8.0),
child: ListView(
children:
item.map((element)=>Text(element)).toList(),
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
createAlertDialog(context).then((onValue) {
temp=onValue;
print(temp);
});
setState(() {
item.add(temp);
print(item);
});
},
tooltip: 'Add URL',
child: Icon(Icons.add),
),
);
You have to call setState() in order to update a Widget if you have new information.
Try changing your showDialog() to this:
showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: Text("Enter URL: "),
content: TextField(
controller: customController,
),
actions: [
MaterialButton(
elevation: 5.0,
child: Text("OK"),
onPressed: () {
item.add(customController.text);
setState((){});
Navigator.of(context).pop();
},
)
],
);
});
That should add the element to the item list, update the widget and then pop. The timing between refresh and popping the dialog box is near instantaneous, so that should be smooth.
Furthermore, you might want to use ListView.builder, a class that will display a list that depends on the number of elements of the list of your choice.
With that said, changing the ListView to this could help in the future:
child: ListView.builder(
itemCount: item.length,
itemBuilder: (context, index) {
return Text('${item.index}'),
},
),
You should use await instate of then. And make item as class property. Your List is not updating because every time you called setState the build function rebuild and the value of item set to ['HI'] because of item=['HI'] this line.
Again when you use then function only the code inside of then function will execute when future will complete. That's why your setState called before your dialog finished.
Here I make some change of your code:
List item = List();
String temp;
Future<String> createAlertDialog(BuildContext context) {
//promise to return string
TextEditingController customController =
TextEditingController(); //new texteditingc object
return showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: Text("Enter URL: "),
content: TextField(
controller: customController,
),
actions: [
MaterialButton(
elevation: 5.0,
child: Text("OK"),
onPressed: () {
Navigator.of(context).pop(customController.text.toString());
},
)
],
);
});
}
#override
void initState() {
item = ['HI'];
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Shortie"),
),
body: Padding(
padding: const EdgeInsets.all(8.0),
child: ListView(
children: item.map((element) => Text(element)).toList(),
),
),
floatingActionButton: FloatingActionButton(
onPressed: () async {
temp = await createAlertDialog(context);
setState(() {
item.add(temp);
print(item);
});
},
tooltip: 'Add URL',
child: Icon(Icons.add),
),
);
}
You could use the provider package and communicate the AlertDialog with the ListView by using notifyListeners() and making the ListView a Consumer of the Provider data.
For more info about the package: https://pub.dev/documentation/provider/latest

Categories

Resources