I want to view the PDF file when the user taps on the cover - flutter

I want to view the PDF file when the user taps on the cover. I am new to Flutter.
Can you guys find out whats wrong in my code? When I tap on the book, it does nothing.
I think the problem is in the function for the PDF Viewer.
I am using advance_pdf_viewer 1.1.6.
class Books extends StatefulWidget {
#override
_BooksState createState() => _BooksState();
}
class _BooksState extends State<Books> {
bool _isLoading = true;
PDFDocument document;
var url;
#override
void initState() {
super.initState();
loadDocument();
}
loadDocument() async {
document = await PDFDocument.fromURL(url);
setState(() => _isLoading = false);
}
changePDF(value) async {
setState(() => _isLoading = true);
if (value == 1) {
document = await PDFDocument.fromURL(url);
} else {
print('nothing');
}
setState(() => _isLoading = false);
}
#override
Widget build(BuildContext context) {
return StreamBuilder(
stream: Firestore.instance.collection('books').snapshots(),
builder: (
context,
snapshot,
) {
if (snapshot.data == null)
return Center(
child: CircularProgressIndicator(
backgroundColor: Colors.red,
valueColor: new AlwaysStoppedAnimation<Color>(Colors.teal),
),
);
return GridView.builder(
shrinkWrap: true,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3, childAspectRatio: 0.7),
itemCount: snapshot.data.documents.length,
itemBuilder: (context, index) => Padding(
padding: const EdgeInsets.all(8.0),
child: GridTile(
child: InkWell(
onTap: () async {
PDFDocument.fromURL(snapshot.data.documents[index]['url']);
_isLoading
? Center(child: CircularProgressIndicator())
: PDFViewer(document: document);
},
child: Container(
height: 200,
width: 110,
decoration: BoxDecoration(
boxShadow: [
BoxShadow(
color: Colors.red[500].withOpacity(0.6),
spreadRadius: 0.5,
blurRadius: 1,
offset: Offset(2, 0),
),
],
color: Colors.white,
borderRadius: BorderRadius.circular(3),
border: Border.all(
style: BorderStyle.solid,
color: Colors.red[500],
width: 0.3)),
child: Column(children: <Widget>[
Padding(
padding: const EdgeInsets.all(5.0),
child: Container(
child: Image.network(
snapshot.data.documents[index]['image'],
width: 100,
),
),
),
SizedBox(height: 5),
Text(
snapshot.data.documents[index]['name'],
)
]),
),
),
),
),
);
});
}
}

PDFViewer returns a Widget. If you want to view the pdf file when tapping on the InkWell you need to make a widget that displays the widget that PDFViewer returns, e.g.,
class PDFScreen extends StatelessWidget {
PDFDocument document;
PDFScreen({#required this.document});
#override
Widget build(BuildContext context) {
return Scaffold(
child: PDFViewer(document: document)
);
}
}
And change the onTap() of the InkWell to:
onTap: () async {
PDFDocument.fromURL(snapshot.data.documents[index]['url']);
_isLoading
? Center(child: CircularProgressIndicator())
: Navigator.push(context, MaterialPageRoute(builder: (context) => PDFScreen(document: document)));
},

Related

how to implement refresh indicator in flutter

'''
I wanted to implement refresh indicator to reload data's from Api(Json). below code runs well but
pull down to refresh is not working. i tried to get the data's initially using initState and then view the data's using future builder. how can i achieve the refresh indicator to reload data's. what is missing from my code?
import 'package:flutter/material.dart';
import '../networkhelper/property.dart';
import '../widgets/recommended.dart';
import '../model/property.dart';
import '../shimmer/recommended.dart';
import '../widgets/home_top.dart';
class Home extends StatefulWidget {
const Home({Key? key}) : super(key: key);
#override
State<Home> createState() => _HomeState();
}
class _HomeState extends State<Home> {
late Future<List<PropertyModel>> _propertyData;
#override
void initState() {
_propertyData = PropertyApi.getProperty();
super.initState();
}
Future<void> _refreshData() async {
setState(() {
_propertyData = PropertyApi.getProperty();
});
await _propertyData;
}
#override
Widget build(BuildContext context) {
Size _screenSize = MediaQuery.of(context).size;
return Scaffold(
backgroundColor: Colors.blue,
body: RefreshIndicator(
onRefresh: () => _refreshData(),
child: ListView(children: [
HomeTopView(),
Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(8),
topRight: Radius.circular(8),
),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: _screenSize.height * 0.02),
Padding(
padding: EdgeInsets.only(left: _screenSize.width * 0.04),
child: Text(
"Recommanded",
style: TextStyle(
fontSize: _screenSize.width * 0.055,
color: Colors.blue,
fontWeight: FontWeight.bold,
),
),
),
SizedBox(height: _screenSize.height * 0.01),
SizedBox(
height: _screenSize.height * 0.3,
child: FutureBuilder<List<PropertyModel>>(
future: _propertyData,
builder: (context, snapshot) {
if (snapshot.hasData) {
List<PropertyModel>? data = snapshot.data;
return ListView.builder(
shrinkWrap: true,
padding: EdgeInsets.only(
left: _screenSize.width * 0.015),
scrollDirection: Axis.horizontal,
physics: const BouncingScrollPhysics(),
itemCount: data!.length,
itemBuilder: (context, index) {
return RecommendedItems(
id: data[index].id,
propertyName: data[index].name,
price: data[index].price,
imgUrl: data[index].img[2],
bedRoomQuantity: data[index].details.bedroom,
bathRoomQuantity: data[index].details.bathroom,
propertyArea: data[index].details.area,
address: data[index].address.city,
propertyType: data[index].details.propertytype,
);
},
);
} else if (snapshot.hasError) {
return Center(
child:
Text("Please Check Your Internet Connection"),
);
}
// By default show a loading spinner.
return ListView.builder(
itemCount: 4,
shrinkWrap: true,
padding: const EdgeInsets.only(left: 10),
scrollDirection: Axis.horizontal,
physics: const BouncingScrollPhysics(),
itemBuilder: (ctx, index) {
return RecommendedShimmer();
});
},
),
),
]),
),
// ),
]),
),
);
}
}
'''
Try changing your setState() method to this:
setState(() async {
_propertyData = await PropertyApi.getProperty();
});
Try this :
Future<void> _refreshData() async {
_propertyData = PropertyApi.getProperty();
setState(() {});
return;
}
This code works smoothly.
Future<void> _refreshData() async {
_propertyData = PropertyApi.getProperty();
await _propertyData;
setState(() {});
}

Flutter Cubit fetching and displaying data

I'm trying to fetch data from Genshin API, code below is working, but only with delay (in GenshinCubit class), it looks weard, because I don't know how much time to set for delay. I think, there is a problem in code, cause it must not set the GenshinLoaded state before the loadedList is completed. Now, if I remove the delay, it just sets the GenshinLoaded when the list is still in work and not completed, await doesn't help. Because of that I get a white screen and need to hot reload for my list to display.
class Repository {
final String characters = 'https://api.genshin.dev/characters/';
Future<List<Character>> getCharactersList() async {
List<Character> charactersList = [];
List<String> links = [];
final response = await http.get(Uri.parse(characters));```
List<dynamic> json = jsonDecode(response.body);
json.forEach((element) {
links.add('$characters$element');
});
links.forEach((element) async {
final response2 = await http.get(Uri.parse(element));
dynamic json2 = jsonDecode(response2.body);
charactersList.add(Character.fromJson(json2));
});
return charactersList;
}
}
class GenshinCubit extends Cubit<GenshinState> {
final Repository repository;
GenshinCubit(this.repository) : super(GenshinInitial(),);
getCharacters() async {
try {
emit(GenshinLoading());
List<Character> list = await repository.getCharactersList();
await Future<void>.delayed(const Duration(milliseconds: 1000));
emit(GenshinLoaded(loadedList: list));
}catch (e) {
print(e);
emit(GenshinError());
}
}
}
class HomeScreen extends StatelessWidget {
final userRepository = Repository();
HomeScreen({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return BlocProvider<GenshinCubit>(
create: (context) => GenshinCubit(userRepository)..getCharacters(),
child: MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(body: Container(child: const CharactersScreen())),
),
);
}
}
class CharactersScreen extends StatefulWidget {
const CharactersScreen({
Key? key,
}) : super(key: key);
#override
State<CharactersScreen> createState() => _CharactersScreenState();
}
class _CharactersScreenState extends State<CharactersScreen> {
#override
Widget build(BuildContext context) {
return Column(
children: [
BlocBuilder<GenshinCubit, GenshinState>(
builder: (context, state) {
if (state is GenshinLoading) {
return Center(
child: CircularProgressIndicator(),
);
}
if (state is GenshinLoaded) {
return SafeArea(
top: false,
child: Column(
children: [
Container(
color: Colors.black,
height: MediaQuery.of(context).size.height,
child: ListView.builder(
scrollDirection: Axis.horizontal,
shrinkWrap: true,
itemCount: state.loadedList.length,
itemBuilder: ((context, index) {
return Padding(
padding: const EdgeInsets.symmetric(
vertical: 50.0, horizontal: 50),
child: GestureDetector(
onTap: () => Navigator.push(
context,
MaterialPageRoute(
builder: (context) => CharacterDetailsPage(
character: state.loadedList[index],
),
),
),
child: Container(
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
decoration: BoxDecoration(
color: Colors.blueAccent.withOpacity(0.3),
borderRadius: const BorderRadius.all(
Radius.circular(
30,
),
)),
child: Align(
alignment: Alignment.bottomRight,
child: Padding(
padding: const EdgeInsets.only(
right: 30.0, bottom: 30),
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.end,
children: [
Text(
state.loadedList[index].name
.toString(),
style: TextStyle(
color: Colors.black,
fontSize: 50),
),
RatingBarIndicator(
itemPadding: EdgeInsets.zero,
rating: double.parse(
state.loadedList[index].rarity
.toString(),
),
itemCount: int.parse(
state.loadedList[index].rarity
.toString(),
),
itemBuilder: (context, index) =>
Icon(
Icons.star_rate_rounded,
color: Colors.amber,
))
],
),
),
),
),
),
);
})),
),
],
),
);
}
if (state is GenshinInitial) {
return Text('Start');
}
if (state is GenshinError) {
return Text('Error');
}
return Text('Meow');
}),
],
);
}
}
I found a solution!
I've got that problem because of forEach. How to wait for forEach to complete with asynchronous callbacks? - there is a solution.

Why i am not able to use setState Under GestureDetector

Why I am not able to use setState Under GestureDetector Using onTap:
After I use setState I got an error like: The function 'setState' isn't defined.
Try importing the library that defines 'setState', And VS Code editor show me
Error like: correcting the name to the name of an existing function, or defining a function named 'setState'.dart(undefined_function).
I try to fix different ways please tell me where is my problem
Thank you
Some Flutter Import Link ::::::::::
class ParsingMap extends StatefulWidget {
const ParsingMap({Key? key}) : super(key: key);
#override
_ParsingMapState createState() => _ParsingMapState();
}
class _ParsingMapState extends State<ParsingMap> {
Future<ApiList>? data;
#override
void initState() {
super.initState();
Network network = Network("https://fakestoreapi.com/products");
data = network.loadPosts();
// print(data);
setState(() {});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.white,
elevation: 0,
leading: Icon(Icons.arrow_left, color: Colors.black),
actions: [
Icon(Icons.search, color: Colors.black),
SizedBox(width: 10),
Icon(Icons.home_filled, color: Colors.black),
SizedBox(width: 8)
],
),
body: Center(
child: Container(
child: FutureBuilder(
future: data,
builder: (context, AsyncSnapshot<ApiList> snapshot) {
List<Api> allPosts;
if (snapshot.hasData) {
allPosts = snapshot.data!.apis!;
return createListView(allPosts, context);
}
return CircularProgressIndicator();
},
),
),
),
);
}
}
class Network {
final String url;
Network(this.url);
Future<ApiList> loadPosts() async {
final response = await get(Uri.parse(url));
if (response.statusCode == 200) {
// print(response.body);
return ApiList.fromJson(json.decode(response.body));
} else {
throw Exception("Faild To get posts");
}
}
}
Widget createListView(List<Api> data, BuildContext context) {
return ListView(
children: [
Container(
height: 300,
margin: EdgeInsets.symmetric(vertical: 16),
child: ListView.builder(
itemCount: data.length,
scrollDirection: Axis.horizontal,
physics: BouncingScrollPhysics(),
padding: EdgeInsets.symmetric(horizontal: 16),
itemBuilder: (context, index) {
int selectedIndex = 0;
return GestureDetector(
onTap: () {
setState(() {
selectedIndex = index;
});
},
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"${data[index].category}",
style: TextStyle(
fontWeight: FontWeight.bold,
color: selectedIndex == index
? Colors.black
: Colors.black38),
),
Container(
margin: EdgeInsets.only(
top: 5,
),
height: 2,
width: 30,
color: selectedIndex == index
? Colors.black
: Colors.transparent,
)
],
),
),
);
},
),
),
],
);
}
Put top-level createListView function in _ParsingMapState class.
class _ParsingMapState extends State<ParsingMap> {
// ...
Widget createListView(...) {}
}

Url Launcher using Flutter/Dart

Good evening folks,
I'm having a slight issue with URL launcher. Basically, what I am trying to do is get a URL from a Future function (retrieved from an API) and then navigate to that URL once a raised button is pressed. The code I have implemented works, but not without a small, pesky bug. The bug appears for about half a second to a second (I'm assuming until the API returns the URL), then the raised button is drawn on to the screen and works fine; I'm then able to navigate to the site. I'm trying to get rid of the bug completely. To save you some time, the relevant FutureBuilder is the second one. Below is my code:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:url_launcher/url_launcher.dart';
import '../models/yahoo_finance_stock_info.dart';
import '../providers/companies_provider.dart';
class BusinessScreen extends StatefulWidget {
static const routeName = '/business-screen';
#override
_BusinessScreenState createState() => _BusinessScreenState();
}
class _BusinessScreenState extends State<BusinessScreen>
with AutomaticKeepAliveClientMixin<BusinessScreen> {
#override
bool get wantKeepAlive => true;
Future _companyDescription;
Future _urlForUrlLauncher;
var _isInit = true;
#override
void didChangeDependencies() {
if (_isInit) {
final companyTicker = ModalRoute.of(context).settings.arguments as String;
final loadedCompany = Provider.of<Companies>(context, listen: false)
.findByTicker(companyTicker);
_companyDescription =
Companies().getSecurityExtendStats(loadedCompany.tickerSymbol);
_urlForUrlLauncher =
Companies().getHistoricalData(loadedCompany.tickerSymbol);
}
_isInit = false;
super.didChangeDependencies();
}
#override
void initState() {
// TODO: implement initState
super.initState();
_companyDescription;
}
#override
Widget build(BuildContext context) {
super.build(context);
final companyTicker = ModalRoute.of(context).settings.arguments as String;
final loadedCompany = Provider.of<Companies>(context, listen: false)
.findByTicker(companyTicker);
return Container(
color: Colors.black87,
child: Column(
children: <Widget>[
Container(
height: 300,
width: double.infinity,
color: Colors.white,
padding: EdgeInsets.all(10),
child: SingleChildScrollView(
padding: EdgeInsets.only(left: 15),
scrollDirection: Axis.vertical,
child: FutureBuilder<StockInformation>(
future: _companyDescription,
builder: (BuildContext context,
AsyncSnapshot<StockInformation> snapshot) {
if (snapshot.data != null) {
return Text(snapshot.data.assetProfile.longBusinessSummary);
}
return Container(
height: 300,
child: Center(
child: CircularProgressIndicator(),
),
);
},
),
),
),
Container(
height: 75,
width: double.infinity,
child: Center(
child: FutureBuilder(
future: _urlForUrlLauncher,
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.data[0]['finalLink'] != null) {
String url10K = snapshot.data[0]['finalLink'];
return RaisedButton(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
side: BorderSide(
color: Colors.grey,
width: 1,
),
),
onPressed: () async {
var url = url10K;
if (await canLaunch(url)) {
await launch(url);
} else {
throw 'Could not launch $url';
}
},
child: Text(
'Go to company\'s filings',
style: TextStyle(
color: Colors.white,
),
),
);
} else {
return RaisedButton(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
side: BorderSide(
color: Colors.grey,
width: 1,
),
),
onPressed: null,
child: Text(
'Go to company\'s filings',
style: TextStyle(color: Colors.white,),
),
);
}
},
),
),
)
],
),
);
}
}
Thanks in advance for any and all help with this issue!
In the second FutureBuilder change the condition,
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.data != null && snapshot.data[0]['finalLink'] != null) {
// raised button with onpressed
} else {
// raised button with onpressed as null
}
}

Flutter - Returning to previous page from AppBar is not refreshing the page, with Navigator.pop(context)

I was trying to get the list page refreshed if a method was run on another page. I do pass the context using the push navigation.
I tried to follow these 3 answers Answer 1 Answer 2 and Answer 3 and I am not able to manage the states here.
This is the first list page which needs to be refreshed. It calls a class
class _PageLocalState extends State<PageLocal> {
#override
Widget build(BuildContext context) {
return Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Expanded(
child: SafeArea(
child: ListView.builder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemCount: widget.allLocal.length,
//padding: const EdgeInsets.only(top: 10.0),
itemBuilder: (context, index) {
return LocalCard(widget.allLocal[index]);
},
)),
)
],
),
);
}
}
The next class:
class LocalCardState extends State<LocalCard> {
FavData localdet;
LocalCardState(this.localdet);
ListTile makeListTile() => ListTile(
contentPadding: EdgeInsets.symmetric(horizontal: 20.0, vertical: 10.0),
title: Text(
localdet.name,
style: TextStyle(fontWeight: FontWeight.bold),
),
subtitle: Text(localdet.loc),
trailing: Icon(Icons.keyboard_arrow_right, size: 30.0),
onTap: () => navigateToDetail(localdet),
);
Widget get localCard {
return new Card(
elevation: 4.0,
margin: new EdgeInsets.symmetric(horizontal: 10.0, vertical: 6.0),
child: Container(
child: makeListTile(),
));
}
#override
Widget build(BuildContext context) {
return new Container(
child: localCard,
);
}
navigateToDetail(FavData localdet) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => FavouriteDetailPage(
mndet: localdet,
)));
setState(() {});
}
}
Now this is routing to the final detail page:
class _FavouriteDetailPageState extends State<FavouriteDetailPage> {
bool isFav = false;
FavData mndet;
_FavouriteDetailPageState(this.mndet);
// reference to our single class that manages the database
final dbHelper = DatabaseHelper.instance;
#override
Widget build(BuildContext context) {
Widget heading = new Container(...);
Widget middleSection = new Expanded(...);
Widget bottomBanner = new Container(...);
Widget body = new Column(...);
final makeBottom = Container(
height: 55.0,
child: BottomAppBar(
color: Color.fromRGBO(36, 36, 36, 1.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
new FavIconWidget(mndet),
],
),
),
);
return Scaffold(
appBar: AppBar(
centerTitle: true,
title: Text('The Details'),
backgroundColor: Color.fromRGBO(36, 36, 36, 1.0),
),
body: Container(
child: Card(
elevation: 5.0,
shape: RoundedRectangleBorder(
side: BorderSide(color: Colors.white70, width: 1),
borderRadius: BorderRadius.circular(10),
),
margin: EdgeInsets.all(20.0),
child: Padding(
padding: new EdgeInsets.symmetric(vertical: 16.0, horizontal: 16.0),
child: body,
),
),
),
bottomNavigationBar: makeBottom,
);
}
void share(BuildContext context, FavData mndet) {
final RenderBox box = context.findRenderObject();
final String shareText = "${mndet.name} - ${mndet.desc}";
Share.share(shareText,
subject: mndet.loc,
sharePositionOrigin: box.localToGlobal(Offset.zero) & box.size);
}
}
class FavIconWidget extends StatefulWidget {
final FavData mnforIcon;
FavIconWidget(this.mnforIcon);
#override
_FavIconWidgetState createState() => _FavIconWidgetState();
}
class _FavIconWidgetState extends State<FavIconWidget> {
final dbHelper = DatabaseHelper.instance;
Future<bool> get isFav async {
final rowsPresent = await dbHelper.queryForFav(widget.mnforIcon.id);
if (rowsPresent > 0) {
print('Card Loaded - Its Favourite already');
return false;
} else {
print('Card Loaded - It is not favourite yet');
return true;
}
}
void _insert() async {...}
void _delete() async {...}
#override
Widget build(BuildContext context) {
return FutureBuilder<bool>(
future: isFav,
initialData:
false, // you can define an initial value while the db returns the real value
builder: (context, snapshot) {
if (snapshot.hasError)
return const Icon(Icons.error,
color: Colors.red); //just in case the db return an error
if (snapshot.hasData)
return IconButton(
icon: snapshot.data
? const Icon(Icons.favorite_border, color: Colors.white)
: Icon(Icons.favorite, color: Colors.red),
onPressed: () => setState(() {
if (!snapshot.data) {
print('Its favourite so deleting it.');
_delete();
} else {
print('Wasnt fav in the first place so inserting.');
_insert();
}
}));
return CircularProgressIndicator(); //if there is no initial value and the future is not yet complete
});
}
}
I am sure this is just some silly coding I have done but just not able to find out. Where.
I tried adding Navigator.pop(context); in different sections of the detail page and it fails.
Currently, I have to navigate back to the Favourites list page and then HomePage and then back to Favourites ListPage to refresh the list.
try this.. Anywhere you are using Navigator.pop or Navigator.push .. Instead of this use this:
Navigator.pushReplacement(context,
MaterialPageRoute(builder: (BuildContext context) => Password())
);
//instead of Password use the name of the page(the second page you want to go to)