Flutter permission to contact - android

My code is the file name is interface.dart. I'm calling the interface.dart from the main.dart
I'm getting a permission denied error when try to access the contacts in the emulator
I added the permission_handler dependency in the pubspec.yaml
Then added the permissions in the AndroidManifest.xml for both read and write
import 'package:flutter/foundation.dart';
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> {
var status = Permission.contacts.status;
Contact _selectedContact = Contact();
late bool _isTrue;
#override
void initState() {
super.initState();
_readJson();
}
_selectContact() async {
List<Contact> contact =
await ContactsService.getContacts(withThumbnails: false);
setState(() {
_selectedContact = contact as Contact;
});
_saveContactToFile(_selectedContact);
}
_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()));
}
_readJson() async {
final directory = await getApplicationDocumentsDirectory();
final file = File('${directory.path}/true.json');
if (!(await file.exists())) {
file.createSync();
file.writeAsStringSync('{"isTrue":true}');
}
final content = jsonDecode(await file.readAsString());
setState(() {
_isTrue = content['isTrue'];
});
}
_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?');
Navigator.of(context).pop();
}),
InkWell(
child: const Text('Did you have your lunch ?'),
onTap: () {
_sendMessage('Did you have your lunch ?');
Navigator.of(context).pop();
}),
InkWell(
child: const Text("What's for dinner?"),
onTap: () {
_sendMessage("What's for dinner?");
Navigator.of(context).pop();
}),
],
),
),
);
},
);
}
}
String? encodeQueryParameters(Map<String, String> params) {
return params.entries
.map((e) =>
'${Uri.encodeComponent(e.key)}=${Uri.encodeComponent(e.value)}')
.join('&');
}
_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';
}
}
#override
Widget build(BuildContext context) {
return MediaQuery(
data: MediaQueryData.fromWindow(WidgetsBinding.instance.window),
child: Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
if (_selectedContact != null)
Text(_selectedContact.displayName ?? 'No contact selected')
else
const Text('No contact selected'),
ElevatedButton(
onPressed: _selectContact,
child: const Text('Select Contact'),
),
ElevatedButton(
onPressed: _promptMessage,
child: const Text('Prompt Message'),
),
],
),
),
));
}
}

You are facing this issues because you haven't asked for the permission yet, refer the code below:
import 'package:flutter/foundation.dart';
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> {
var status = Permission.contacts.status;
Contact _selectedContact = Contact();
late bool _isTrue;
#override
void initState() {
_readJson();
super.initState();
}
_selectContact() async {
if (await Permission.contacts.request().isGranted) {
List<Contact> contact =
await ContactsService.getContacts(withThumbnails: false);
setState(() {
_selectedContact = contact as Contact;
});
_saveContactToFile(_selectedContact);
}
}
_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()));
}
_readJson() async {
final directory = await getApplicationDocumentsDirectory();
final file = File('${directory.path}/true.json');
if (!(await file.exists())) {
file.createSync();
file.writeAsStringSync('{"isTrue":true}');
}
final content = jsonDecode(await file.readAsString());
setState(() {
_isTrue = content['isTrue'];
});
}
_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?');
Navigator.of(context).pop();
}),
InkWell(
child: const Text('Did you have your lunch ?'),
onTap: () {
_sendMessage('Did you have your lunch ?');
Navigator.of(context).pop();
}),
InkWell(
child: const Text("What's for dinner?"),
onTap: () {
_sendMessage("What's for dinner?");
Navigator.of(context).pop();
}),
],
),
),
);
},
);
}
}
String? encodeQueryParameters(Map<String, String> params) {
return params.entries
.map((e) =>
'${Uri.encodeComponent(e.key)}=${Uri.encodeComponent(e.value)}')
.join('&');
}
_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';
}
}
#override
Widget build(BuildContext context) {
return MediaQuery(
data: MediaQueryData.fromWindow(WidgetsBinding.instance.window),
child: Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
if (_selectedContact != null)
Text(_selectedContact.displayName ?? 'No contact selected')
else
const Text('No contact selected'),
ElevatedButton(
onPressed: _selectContact,
child: const Text('Select Contact'),
),
ElevatedButton(
onPressed: _promptMessage,
child: const Text('Prompt Message'),
),
],
),
),
));
}
}

Check the permission status first as this:-
final status = Permission.contacts.request()
if(status.isGrantet){
// permission has granted now save the contact here
}
Refer this link for more details.

Related

How can I change a background screen with a photo fetched from api in flutter?

I'm fetching images from https://pixabay.com/api/docs/ this web-page.
I'm trying to save that image with onTap() {} with Shared Preferences package and set as background on previous page. I tried to make Shared preferences class seperate but it was showing errors. So pref.setString() method I did in ontap() {} and pref.getString() in my Center() widget with ternary operator to show that if image url is empty, write text, if it contains image url, show image. I tried to put it into setState() {} method but there are all red squiggles. How can I achieve this?
Thank you very much in advance.
Here is my code:
this is the page I'm trying to set background image:
import 'package:abcde/authentication_screen/pages/photos_page.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
class AfterAuth extends StatefulWidget {
AfterAuth({Key? key}) : super(key: key);
static const String pathId = 'Welcome Page';
#override
State<AfterAuth> createState() => _AfterAuthState();
}
class _AfterAuthState extends State<AfterAuth> {
String welcomeScreen = 'Welcome to the club';
#override
void initState() {
// TODO: implement initState
super.initState();
//final prefs = await SharedPreferences.getInstance();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Welcome'),
),
drawer: Drawer(
backgroundColor: Colors.white,
child: ListView(
padding: EdgeInsets.zero,
children: [
const DrawerHeader(
decoration: BoxDecoration(
color: Colors.lightBlueAccent,
),
child: Text('Welcome'),
),
ListTile(
title: const Text(
'Bonjour',
),
onTap: () {
setState(() {
welcomeScreen = 'Bonjour';
});
Navigator.pop(context);
},
),
ListTile(
title: const Text('Hello'),
onTap: () {
setState(() {
welcomeScreen = 'Hello';
});
Navigator.pop(context);
},
),
ListTile(
title: const Text('Guten Tag'),
onTap: () {
setState(() {
welcomeScreen = 'Guten Tag';
});
Navigator.pop(context);
},
),
ListTile(
title: const Text('Au Revoir'),
onTap: () {
setState(() {
welcomeScreen = 'Au Revoir';
});
Navigator.pop(context);
},
),
ListTile(
title: const Text('Chao Bambino'),
onTap: () {
setState(() {
welcomeScreen = 'Chao Bambino';
});
Navigator.pop(context);
},
),
],
),
),
body: Center(
child: address == ''
? Text(welcomeScreen)
: Image(
image: NetworkImage(address),
),
),
floatingActionButton: FloatingActionButton(
backgroundColor: Colors.lightBlue,
onPressed: () {
Navigator.pushNamed(context, PhotosPage.pathId);
},
child: const Icon(
Icons.add,
color: Colors.white,
),
),
);
}
String address = '';
internalMemory() async {
final prefs = await SharedPreferences.getInstance();
address = prefs.getString('urlAddress')!;
/* if(address!.isNotEmpty) {
isAddressEmpty = false;
}*/
return address;
}
}
this is the page where I fetch images from api and trying to save with Shared Preferences. actually not sure if it saves and not sure how to check it:
import 'package:flutter/material.dart';
import '../../services/photos_service.dart';
import 'package:shared_preferences/shared_preferences.dart';
class PhotosPage extends StatefulWidget {
const PhotosPage({Key? key}) : super(key: key);
static const String pathId = 'Photos page';
#override
State<PhotosPage> createState() => _PhotosPageState();
}
class _PhotosPageState extends State<PhotosPage> {
PhotosService photosService = PhotosService();
#override
void initState() {
super.initState();
photosService.getPhotos();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Photos'),
),
body: Padding(
padding: const EdgeInsets.all(16),
child: Column(children: [
_getProductTypeList(),
]),
),
);
}
Widget _buildListItem(
BuildContext context, AsyncSnapshot<dynamic> snapshot, int index) {
final photoItem = snapshot.data[index].previewURL;
print('photoItem is $photoItem');
return photoCard(context, photoItem);
}
Widget _buildList(BuildContext context, AsyncSnapshot<dynamic> snapshot) {
var values = snapshot.data;
return ListView.builder(
itemCount: snapshot.hasData ? snapshot.data.length : 0,
itemBuilder: (context, index) {
return _buildListItem(context, snapshot, index);
},
);
}
Widget _getProductTypeList() {
return Expanded(
child: FutureBuilder(
future: photosService.getPhotos(),
builder: (BuildContext context, AsyncSnapshot<dynamic> snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(
child: LinearProgressIndicator(),
);
}
return _buildList(context, snapshot);
},
),
);
}
}
Widget photoCard(BuildContext context, String url) {
return GestureDetector(
onTap: () async {
final prefs = await SharedPreferences.getInstance();
await prefs.setString('urlAddress', url);
const snackBar =
SnackBar(content: Text('You saved image'));
ScaffoldMessenger.of(context).showSnackBar(snackBar);
},
child: Card(
child: Image(
image: NetworkImage(url),
),
),
);
}
this is my service/model
import 'dart:convert';
import 'package:http/http.dart';
const apiKey = '26711456-bde74f403cb42e77029bc1678';
const appUrl = 'https://pixabay.com/api/';
class PhotosService {
Future getData(String url) async {
print('Calling url: $url');
final response = await get(Uri.parse(url));
if (response.statusCode == 200) {
return response.body;
} else {
print(response.statusCode);
}
}
String? query;
List<PhotosModel> dataList = [];
Future<List<PhotosModel>> getPhotos() async {
final photosData = await getData('$appUrl?key=$apiKey&q=$query');
var data = json.decode(photosData);
var items = data["hits"];
items.forEach((element) {
dataList.add(PhotosModel.fromJson(element));
});
print('this is photos data: $photosData');
return dataList;
}
}
class PhotosModel {
String previewURL;
PhotosModel({required this.previewURL});
factory PhotosModel.fromJson(Map<dynamic, dynamic> json) =>
_commentFromJson(json);
Map<dynamic, dynamic> toJson() => photosModelToJson(this);
}
PhotosModel _commentFromJson(Map<dynamic, dynamic> json) {
return PhotosModel(
previewURL: json['previewURL'],
);
}
Map<dynamic, dynamic> photosModelToJson(PhotosModel instance) =>
<dynamic, dynamic>{
'previewURL': instance.previewURL,
};
Call your function on initState to set the background on restart the app.
#override
void initState() {
// TODO: implement initState
super.initState();
internalMemory();
//final prefs = await SharedPreferences.getInstance();
}
add setstate to your internalMemory() function to set background
internalMemory() async {
final prefs = await SharedPreferences.getInstance();
address = await prefs.getString('urlAddress')!;
setState((){});
}
also, add the function below to set the background when you navigate back.
floatingActionButton: FloatingActionButton(
backgroundColor: Colors.lightBlue,
onPressed: () async {
await Navigator.pushNamed(context, PhotosPage.pathId);
internalMemory();
},
child: const Icon(
Icons.add,
color: Colors.white,
),
),

Flutter Multiselect Chips From API Contents

I am using this multi-select flutter package. It already has contents to view chips, but I want to load contents from API to do that instead.
I already added a fetchData() function to download the data from the API. Now, how do I get the JSON data into the chips multi-select?
Here is my code:
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:multiselect_formfield/multiselect_formfield.dart';
import 'package:multi_select_flutter/multi_select_flutter.dart';
import 'package:http/http.dart' as http;
import 'Includes/APILinks.dart';
void main() => runApp(Sample());
class Sample extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
List _myActivities;
String _myActivitiesResult;
final formKey = new GlobalKey<FormState>();
#override
void initState() {
super.initState();
_myActivities = [];
this.fetchData();
}
_saveForm() {
var form = formKey.currentState;
if (form.validate()) {
form.save();
setState(() {
_myActivitiesResult = _myActivities.toString();
});
}
}
fetchData() async{
var url = CategorySection;
var response = await http.get(url);
if(response.statusCode == 200){
var items = json.decode(response.body);
print(items);
setState(() {
_myActivities = items;
});
} else {
setState(() {
_myActivities = [];
});
}
}
#override
Widget build(BuildContext context) {
final double maxWidth = MediaQuery.of(context).size.width;
final double maxHeight = MediaQuery.of(context).size.height;
return Scaffold(
appBar: AppBar(
title: Text('MultiSelect Formfield Example'),
),
body: Center(
child: Form(
key: formKey,
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Container(
height: maxHeight/2,
width: maxWidth,
padding: EdgeInsets.all(16),
child: getListView(),
),
Container(
padding: EdgeInsets.all(8),
child: RaisedButton(
child: Text('Save'),
onPressed: _saveForm,
),
),
Container(
padding: EdgeInsets.all(16),
child: Text(_myActivitiesResult),
)
],
),
),
),
);
}
Widget getListView() {
return ListView.builder(
itemCount: _myActivities.length,
itemBuilder: (context, index){
return cardView(_myActivities[index]);
},
);
}
Widget cardView(item) {
var fullName = item;
return MultiSelectDialogField(
items: _myActivities,
title: Text("Animals"),
selectedColor: Colors.blue,
decoration: BoxDecoration(
color: Colors.blue.withOpacity(0.1),
borderRadius: BorderRadius.all(Radius.circular(40)),
border: Border.all(
color: Colors.blue,
width: 2,
),
),
buttonIcon: Icon(
Icons.pets,
color: Colors.blue,
),
buttonText: Text(
"Favorite Animals",
style: TextStyle(
color: Colors.blue[800],
fontSize: 16,
),
),
onConfirm: (results) {
_myActivities = results;
},
);
}
}
You can use a Future function with the return type that you want, in that case it will be Future<List<MultiSelectItem>>, then you would use async and await, it will be something like that:
Future<List<Item>> fetchItems(BuildContext context) async {
http.Response response = await http.get(
Uri.parse('your link for ur api'),
//you can ignore that part
headers: <String, String>{
'Authorization':
'Token ${Provider.of<Token>(context, listen: false).token}'
});
//here you turn the json to a <String, dynamic> map
var data = jsonDecode(response.body);
List<Item> result = [];
for (var item in data) {
//use your factory if you wanna to parse the data the way you want it
result.add(Item.fromJson(item));
}
//that should be your disered list
return result;
}
The FilterChip class provides a multiple select chip. Using FutureBuilder we can fetch future data and build the chips list. We can also call the API first, and then map the results with the filter chips.
Here is an example of a FilterChip, where chips are populated from an API using FutureBuilder:
import 'dart:convert';
import 'package:filterchip_sample/album.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'FilterChip Sample',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'FilterChip Sample'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
bool favorite = false;
final List<int> _filters = <int>[];
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: SingleChildScrollView(
padding: const EdgeInsets.all(20),
child: Column(
children: <Widget>[
FutureBuilder<List<Album>>(
future: _fetchAlbums(),
builder:
(BuildContext context, AsyncSnapshot<List<Album>> snapshot) {
Widget result;
if (snapshot.hasData) {
result = Wrap(
spacing: 5.0,
children: snapshot.data!.map((Album album) {
return FilterChip(
label: Text(album.title),
selected: _filters.contains(album.id),
onSelected: (bool value) {
setState(() {
if (value) {
if (!_filters.contains(album.id)) {
_filters.add(album.id);
}
} else {
_filters.removeWhere((int id) {
return id == album.id;
});
}
});
},
);
}).toList(),
);
} else if (snapshot.hasError) {
result = Text('Error: ${snapshot.error}');
} else {
result = const Text('Awaiting result...');
}
return result;
},
),
],
),
),
);
}
Future<List<Album>>? _fetchAlbums() async {
final response = await http
.get(Uri.parse('https://jsonplaceholder.typicode.com/albums/'));
if (response.statusCode == 200) {
var json = jsonDecode(response.body);
var albums = List<Album>.from(json.map((i) => Album.fromJson(i)));
return albums;
} else {
throw Exception('Failed to get albums');
}
}
}

The getter 'pokemon' was called on null

I am trying to make a pokemon app. But I get this error:
"The getter 'pokemon' was called on null.
Receiver: null
Tried calling: pokemon"
I am using this API url for data "https://raw.githubusercontent.com/Biuni/PokemonGO-Pokedex/master/pokedex.json"
And my Pokemon class is here:
class PokeHub {
List<Pokemon> pokemon;
PokeHub({this.pokemon});
PokeHub.fromJson(Map<String,dynamic> json) {
if (json['pokemon'] != null) {
pokemon = new List<Pokemon>();
json['pokemon'].forEach((items) {
pokemon.add(new Pokemon.fromJson(items));
});
}
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
if (this.pokemon != null) {
data['pokemon'] = this.pokemon.map((items) => items.toJson()).toList();
}
return data;
}
}
class Pokemon {
int id;
String num;
String name;
String img;
List<String> type;
String height;
String weight;
String candy;
int candyCount;
String egg;
String spawnChance;
String avgSpawns;
String spawnTime;
List<double> multipliers;
List<String> weaknesses;
List<NextEvolution> nextEvolution;
Pokemon(
{this.id,
this.num,
this.name,
this.img,
this.type,
this.height,
this.weight,
this.candy,
this.candyCount,
this.egg,
this.spawnChance,
this.avgSpawns,
this.spawnTime,
this.multipliers,
this.weaknesses,
this.nextEvolution});
Pokemon.fromJson(Map<String, dynamic> json) {
id = json['id'];
num = json['num'];
name = json['name'];
img = json['img'];
type = json['type'].cast<String>();
height = json['height'];
weight = json['weight'];
candy = json['candy'];
candyCount = json['candy_count'];
egg = json['egg'];
spawnChance = json['spawn_chance'].toString();
avgSpawns = json['avg_spawns'].toString();
spawnTime = json['spawn_time'];
multipliers = json['multipliers']?.cast<double>();
weaknesses = json['weaknesses'].cast<String>();
if (json['next_evolution'] != null) {
nextEvolution = new List<NextEvolution>();
json['next_evolution'].forEach((v) {
nextEvolution.add(new NextEvolution.fromJson(v));
});
}
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['id'] = this.id;
data['num'] = this.num;
data['name'] = this.name;
data['img'] = this.img;
data['type'] = this.type;
data['height'] = this.height;
data['weight'] = this.weight;
data['candy'] = this.candy;
data['candy_count'] = this.candyCount;
data['egg'] = this.egg;
data['spawn_chance'] = this.spawnChance;
data['avg_spawns'] = this.avgSpawns;
data['spawn_time'] = this.spawnTime;
data['multipliers'] = this.multipliers;
data['weaknesses'] = this.weaknesses;
if (this.nextEvolution != null) {
data['next_evolution'] =
this.nextEvolution.map((v) => v.toJson()).toList();
}
return data;
}
}
class NextEvolution {
String num;
String name;
NextEvolution({this.num, this.name});
NextEvolution.fromJson(Map<String, dynamic> json) {
num = json['num'];
name = json['name'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['num'] = this.num;
data['name'] = this.name;
return data;
}
And this is my main.dart file:
import 'package:flutter/material.dart';
import 'package:pokemon_flutter/screens/main_screen.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: "Poke App",
home: MainScreen(),
debugShowCheckedModeBanner: false,
);
}
}
And this is my main_screen.dart file:
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
import 'package:pokemon_flutter/models/pokemon.dart';
class MainScreen extends StatefulWidget {
#override
_MainScreenState createState() => _MainScreenState();
}
class _MainScreenState extends State<MainScreen> {
var url =
"https://raw.githubusercontent.com/Biuni/PokemonGO-Pokedex/master/pokedex.json";
PokeHub pokeHub;
#override
void initState() {
super.initState();
fetchData();
}
void fetchData() async {
var res = await http.get(url);
var decodedJson = jsonDecode(res.body);
pokeHub = PokeHub.fromJson(decodedJson);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Poke App"),
backgroundColor: Colors.cyan,
),
body: GridView.count(
crossAxisCount: 2,
children: pokeHub.pokemon.map((poke) => Card()).toList(),
),
drawer: Drawer(),
floatingActionButton: FloatingActionButton(
onPressed: () {},
backgroundColor: Colors.cyan,
child: Icon(Icons.refresh),
),
);
}
}
It seems like all things are true thats why I can not recognized the fault. If you have a advice for this situation I'll be appreciated.
Edit: I am new here thats why sometimes my questions could be meanless.But please dont decrease my points. Stackoverflow not gonna accept my questions anymore. Please increase my points.
when I finished my code the aplication worked. Here is my new main_screen.dart file:
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
import 'package:pokemon_flutter/models/pokemon.dart';
import 'package:pokemon_flutter/screens/pokemon_detail_screen.dart';
class MainScreen extends StatefulWidget {
#override
_MainScreenState createState() => _MainScreenState();
}
class _MainScreenState extends State<MainScreen> {
var url =
"https://raw.githubusercontent.com/Biuni/PokemonGO-Pokedex/master/pokedex.json";
PokeHub pokeHub;
#override
void initState() {
super.initState();
fetchData();
}
var res;
void fetchData() async {
res= await http.get(url);
var decodedJson = jsonDecode(res.body);
pokeHub = PokeHub.fromJson(decodedJson);
print(pokeHub.toJson());
setState(() {});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Poke App"),
backgroundColor: Colors.cyan,
),
body: pokeHub == null
? Center(
child: CircularProgressIndicator(),
)
: GridView.count(
crossAxisCount: 2,
children: pokeHub.pokemon
.map((poke) => Padding(
padding: const EdgeInsets.all(2.0),
child: InkWell(
onTap: (){
Navigator.push(context, MaterialPageRoute(builder: (context)=>PokeDetail(
pokemon: poke,
)));
},
child: Hero(
tag: poke.img,
child: Card(
elevation: 3.0,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Container(
height: 100.0,
width: 100.0,
decoration: BoxDecoration(
image: DecorationImage(
image: NetworkImage(poke.img))),
),
Text(
poke.name,
style: TextStyle(
fontSize: 20.0,
fontWeight: FontWeight.bold,
),
)
],
),
),
),
),
))
.toList(),
),
drawer: Drawer(),
floatingActionButton: FloatingActionButton(
onPressed: () {},
backgroundColor: Colors.cyan,
child: Icon(Icons.refresh),
),
);
}
}
I added if else statement and set state. I don't know how I solved this problem but it was solved.
I think this codes are worked:
void fetchData() async {
res= await http.get(url);
var decodedJson = jsonDecode(res.body);
pokeHub = PokeHub.fromJson(decodedJson);
print(pokeHub.toJson());
setState(() {});
}
body: pokeHub == null
? Center(
child: CircularProgressIndicator(),
)
: GridView.count(
When you call pokeHub.pokemon, pokeHub is still null so it can't return any value for pokemon getter.
I see that you are fetching data, but when your widget is built, this data (which is returned asynchronously) hasn't been returned yet. For this case, it will be a good choice using FutureBuilder. Something like this should work:
var res;
void fetchData() {
res = http.get(url).then((value){
var decodedJson = jsonDecode(res.body);
pokeHub = PokeHub.fromJson(decodedJson);
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Poke App"),
backgroundColor: Colors.cyan,
),
body: FutureBuilder(
future: res,
builder: (BuildContext context, AsyncSnapshot snapshot) {
if(snapshot.hasData)
return GridView.count(
crossAxisCount: 2,
children: pokeHub.pokemon.map((poke) => Card()).toList(),
),
drawer: Drawer(),
floatingActionButton: FloatingActionButton(
onPressed: () {},
backgroundColor: Colors.cyan,
child: Icon(Icons.refresh),
);
},
),
);
}
This (kind of) uses the futurebuilder to build the widgets only once the data has been retrieved. More info: https://api.flutter.dev/flutter/widgets/FutureBuilder-class.html

Flutter: 'Future<dynamic>' is not a subtype of type 'String'

I'm trying to build a URL shortner using the following api: https://shrtco.de/docs/. I parsed the JSON file and tried to rebuild the listview. But after adding the url I'm getting the above error. The api seems to be working fine when I print it though. I don't know whats wrong. I'm a beginner in Flutter and Dart. Please help me.
class _homePageState extends State<homePage> {
getdata(String userUrl) async {
//JSON Parser
var url = 'https://api.shrtco.de/v2/shorten?url=$userUrl';
var respons = await http.get(url);
var result = jsonDecode(respons.body);
var shortlink = result['result']['short_link']; //dictionary parse
print(shortlink);
return shortlink;
}
Future<String> 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 != "") {
var getShortlink = getdata(customController.text);
item.add(getShortlink);
}
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: ListView.builder(
itemCount: item.length,
itemBuilder: (context, index) {
return ListTile(
leading: Icon(Icons.link),
title: Text(item[index]),
subtitle: Text("data"),
);
//return new Text();
},
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
createAlertDialog(context).then((onValue) {
temp = onValue;
print(temp);
});
},
You can copy paste run full code below
Step 1: getdata return Future<String>
Step 2: onPressed need async await
Step 3: put getShortlink in Navigator.of(context).pop(getShortlink);
code snippet
Future<String> getdata(String userUrl) async {
...
onPressed: () async {
String getShortlink;
if (customController.text != null &&
customController.text != "") {
getShortlink = await getdata(customController.text);
item.add(getShortlink);
}
setState(() {});
Navigator.of(context).pop(getShortlink);
},
working demo
full code
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
List<String> item = [];
Future<String> getdata(String userUrl) async {
//JSON Parser
var url = 'https://api.shrtco.de/v2/shorten?url=$userUrl';
var respons = await http.get(url);
var result = jsonDecode(respons.body);
var shortlink = result['result']['short_link']; //dictionary parse
print(shortlink);
return shortlink;
}
Future<String> createAlertDialog(BuildContext context) {
//method for alertdialog
//promise to return string
TextEditingController customController =
TextEditingController(text: "example.org/very/long/link.html"); //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: () async {
String getShortlink;
if (customController.text != null &&
customController.text != "") {
getShortlink = await getdata(customController.text);
item.add(getShortlink);
}
setState(() {});
Navigator.of(context).pop(getShortlink);
},
)
],
);
});
}
#override
Widget build(BuildContext context) {
String temp;
return Scaffold(
appBar: AppBar(
title: Text("Shortie"),
),
body: Padding(
padding: const EdgeInsets.all(8.0),
child: ListView.builder(
itemCount: item.length,
itemBuilder: (context, index) {
return ListTile(
leading: Icon(Icons.link),
title: Text(item[index]),
subtitle: Text("data"),
);
//return new Text();
},
),
),
floatingActionButton: FloatingActionButton(onPressed: () {
createAlertDialog(context).then((onValue) {
temp = onValue;
print(temp);
});
}));
}
}
createAlertDialog(BuildContext context) doesn't return a Future<String>, it's returns Future<T>, dynamic in your case.
https://api.flutter.dev/flutter/material/showDialog.html

Flutter Error Where Future Already Completed

I'm having trouble figuring out an error of 'Bad state: Future already completed'. I basically have an app that uses a BottomNavigationBar and on one of my screens I am using this class that uses the Completer class.
It happens when creating the webview again:
body: WebView(
initialUrl: 'https://www.google.com/',
onWebViewCreated: (WebViewController webViewController) {
_controller.complete(webViewController);
},
)
Everything loads fine on the first view but if you select another BottomNavigationBarItem and then go back to the class, I get an Exception of Bad state: Future already completed. Below is full code.
import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';
import 'package:url_launcher/url_launcher.dart';
import 'dart:async';
class myClass extends StatelessWidget {
Completer<WebViewController> _controller = Completer<WebViewController>();
final Set<String> _favorites = Set<String>();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
actions: <Widget>[
NavigationControls(_controller.future),
Menu(_controller.future, () => _favorites),
],
),
//EXCEPTION OCCURS HERE
//BAD STATE: FUTURE ALREADY COMPLETED
body: WebView(
initialUrl: 'https://www.google.com/',
onWebViewCreated: (WebViewController webViewController) {
_controller.complete(webViewController);
},
),
floatingActionButton: _bookmarkButton(),
);
}
_bookmarkButton() {
return FutureBuilder<WebViewController>(
future: _controller.future,
builder:
(BuildContext context, AsyncSnapshot<WebViewController> controller) {
if (controller.hasData) {
return FloatingActionButton(
onPressed: () async {
var url = await controller.data.currentUrl();
_favorites.add(url);
Scaffold.of(context).showSnackBar(
SnackBar(content: Text('Saved $url for later reading.')),
);
},
child: Icon(Icons.favorite),
);
}
return Container();
},
);
}
}
class Menu extends StatelessWidget {
Menu(this._webViewControllerFuture, this.favoritesAccessor);
final Future<WebViewController> _webViewControllerFuture;
final Function favoritesAccessor;
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: _webViewControllerFuture,
builder:
(BuildContext context, AsyncSnapshot<WebViewController> controller) {
if (!controller.hasData) return Container();
return PopupMenuButton<String>(
onSelected: (String value) async {
if (value == 'Email link') {
var url = await controller.data.currentUrl();
await launch(
'mailto:?subject=hello&body=$url');
} else {
var newUrl = await Navigator.push(context,
MaterialPageRoute(builder: (BuildContext context) {
return FavoritesPage(favoritesAccessor());
}));
Scaffold.of(context).removeCurrentSnackBar();
if (newUrl != null) controller.data.loadUrl(newUrl);
}
},
itemBuilder: (BuildContext context) => <PopupMenuItem<String>>[
const PopupMenuItem<String>(
value: 'Email link',
child: Text('Email link'),
),
const PopupMenuItem<String>(
value: 'See Favorites',
child: Text('See Favorites'),
),
],
);
},
);
}
}
class FavoritesPage extends StatelessWidget {
FavoritesPage(this.favorites);
final Set<String> favorites;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Favorite pages')),
body: ListView(
children: favorites
.map((url) => ListTile(
title: Text(url), onTap: () => Navigator.pop(context, url)))
.toList()),
);
}
}
class NavigationControls extends StatelessWidget {
const NavigationControls(this._webViewControllerFuture)
: assert(_webViewControllerFuture != null);
final Future<WebViewController> _webViewControllerFuture;
#override
Widget build(BuildContext context) {
return FutureBuilder<WebViewController>(
future: _webViewControllerFuture,
builder:
(BuildContext context, AsyncSnapshot<WebViewController> snapshot) {
final bool webViewReady =
snapshot.connectionState == ConnectionState.done;
final WebViewController controller = snapshot.data;
return Row(
children: <Widget>[
IconButton(
icon: const Icon(Icons.arrow_back_ios),
onPressed: !webViewReady
? null
: () => navigate(context, controller, goBack: true),
),
IconButton(
icon: const Icon(Icons.arrow_forward_ios),
onPressed: !webViewReady
? null
: () => navigate(context, controller, goBack: false),
),
],
);
},
);
}
navigate(BuildContext context, WebViewController controller,
{bool goBack: false}) async {
bool canNavigate =
goBack ? await controller.canGoBack() : await controller.canGoForward();
if (canNavigate) {
goBack ? controller.goBack() : controller.goForward();
} else {
Scaffold.of(context).showSnackBar(
SnackBar(
content: Text("No ${goBack ? 'back' : 'forward'} history item")),
);
}
}
}
tried to reproduce the problem, but wasnt able to do so. Can you show the code with your Bottom Navigation Bar etc? I interpreted it this way, and here it works:
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:webview_flutter/webview_flutter.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
initialRoute: "/",
routes: {
"/": (context) => Home(),
},
);
}
}
class Home extends StatefulWidget {
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
final navigatorKey = GlobalKey<NavigatorState>();
int _currentIndex = 0;
final pagesRouteFactories = {
'page1': (RouteSettings settings) => MaterialPageRoute<dynamic>(
builder: (BuildContext context) => HomePage(),
settings: settings,
),
'page2': (RouteSettings settings) => MaterialPageRoute<dynamic>(
builder: (BuildContext context) => MyClass(),
settings: settings,
),
};
#override
Widget build(BuildContext context) {
return Scaffold(
body: MaterialApp(
navigatorKey: navigatorKey,
onGenerateRoute: (RouteSettings route) =>
pagesRouteFactories[route.name](route),
initialRoute: 'page1',
),
bottomNavigationBar: BottomNavigationBar(
backgroundColor: Colors.white,
type: BottomNavigationBarType.fixed,
currentIndex: _currentIndex,
items: <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.home),
title: FittedBox(
child: Text(
'Home',
style: TextStyle(fontWeight: FontWeight.w600),
),
),
),
BottomNavigationBarItem(
icon: Icon(Icons.web),
title: FittedBox(
child: Text(
'Web',
style: TextStyle(fontWeight: FontWeight.w600),
),
),
),
],
onTap: (int index) {
setState(() {
_currentIndex = index;
});
navigatorKey.currentState
.pushReplacementNamed(pagesRouteFactories.keys.toList()[index]);
},
),
);
}
}
class HomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Center(
child: Text('hi'),
),
);
}
}
class MyClass extends StatelessWidget {
Completer<WebViewController> _controller = Completer<WebViewController>();
final Set<String> _favorites = Set<String>();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
actions: <Widget>[
NavigationControls(_controller.future),
Menu(_controller.future, () => _favorites),
],
),
//EXCEPTION OCCURS HERE
//BAD STATE: FUTURE ALREADY COMPLETED
body: WebView(
initialUrl: 'https://www.google.com/',
onWebViewCreated: (WebViewController webViewController) {
_controller.complete(webViewController);
},
),
floatingActionButton: _bookmarkButton(),
);
}
_bookmarkButton() {
return FutureBuilder<WebViewController>(
future: _controller.future,
builder:
(BuildContext context, AsyncSnapshot<WebViewController> controller) {
if (controller.hasData) {
return FloatingActionButton(
onPressed: () async {
var url = await controller.data.currentUrl();
_favorites.add(url);
Scaffold.of(context).showSnackBar(
SnackBar(content: Text('Saved $url for later reading.')),
);
},
child: Icon(Icons.favorite),
);
}
return Container();
},
);
}
}
class Menu extends StatelessWidget {
Menu(this._webViewControllerFuture, this.favoritesAccessor);
final Future<WebViewController> _webViewControllerFuture;
final Function favoritesAccessor;
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: _webViewControllerFuture,
builder:
(BuildContext context, AsyncSnapshot<WebViewController> controller) {
if (!controller.hasData) return Container();
return PopupMenuButton<String>(
onSelected: (String value) async {
if (value == 'Email link') {
var url = await controller.data.currentUrl();
await launch('mailto:?subject=hello&body=$url');
} else {
var newUrl = await Navigator.push(context,
MaterialPageRoute(builder: (BuildContext context) {
return FavoritesPage(favoritesAccessor());
}));
Scaffold.of(context).removeCurrentSnackBar();
if (newUrl != null) controller.data.loadUrl(newUrl);
}
},
itemBuilder: (BuildContext context) => <PopupMenuItem<String>>[
const PopupMenuItem<String>(
value: 'Email link',
child: Text('Email link'),
),
const PopupMenuItem<String>(
value: 'See Favorites',
child: Text('See Favorites'),
),
],
);
},
);
}
}
class FavoritesPage extends StatelessWidget {
FavoritesPage(this.favorites);
final Set<String> favorites;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Favorite pages')),
body: ListView(
children: favorites
.map((url) => ListTile(
title: Text(url), onTap: () => Navigator.pop(context, url)))
.toList()),
);
}
}
class NavigationControls extends StatelessWidget {
const NavigationControls(this._webViewControllerFuture)
: assert(_webViewControllerFuture != null);
final Future<WebViewController> _webViewControllerFuture;
#override
Widget build(BuildContext context) {
return FutureBuilder<WebViewController>(
future: _webViewControllerFuture,
builder:
(BuildContext context, AsyncSnapshot<WebViewController> snapshot) {
final bool webViewReady =
snapshot.connectionState == ConnectionState.done;
final WebViewController controller = snapshot.data;
return Row(
children: <Widget>[
IconButton(
icon: const Icon(Icons.arrow_back_ios),
onPressed: !webViewReady
? null
: () => navigate(context, controller, goBack: true),
),
IconButton(
icon: const Icon(Icons.arrow_forward_ios),
onPressed: !webViewReady
? null
: () => navigate(context, controller, goBack: false),
),
],
);
},
);
}
navigate(BuildContext context, WebViewController controller,
{bool goBack: false}) async {
bool canNavigate =
goBack ? await controller.canGoBack() : await controller.canGoForward();
if (canNavigate) {
goBack ? controller.goBack() : controller.goForward();
} else {
Scaffold.of(context).showSnackBar(
SnackBar(
content: Text("No ${goBack ? 'back' : 'forward'} history item")),
);
}
}
}

Categories

Resources