Am trying to load a blog on flutter using API. for some reasons, the file is not loading on the emulator, and the code not showing any errors or suggestions. Once i click run, after the connection, it stops abruptly... Please, if anyone can preview the code and and help me out.. The error log is saying something about immutable, which i don't really follow.. Please help.
Main.dart
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
import 'package:html/parser.dart';
import 'pages/post_view.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Flutter Demo',
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: new MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
var _isLoading = true; //For progress bar
var posts;
var imgUrl;
//initialization
void initState() {
super.initState();
_fetchData();
}
//Function to fetch data from JSON
_fetchData() async {
print("attempting");
final url =
"https://www.googleapis.com/blogger/v3/blogs/MY BLOG ID HERE/posts/?key= API KEY HERE";
final response = await http.get(url);
print(response);
if (response.statusCode == 200) {
//HTTP OK is 200
final Map items = json.decode(response.body);
var post = items['items'];
setState(() {
_isLoading = false;
this.posts = post;
});
}
}
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text("Blogger"),
actions: <Widget>[
new IconButton(
icon: new Icon(Icons.refresh),
onPressed: () {
setState(() {
_isLoading = true;
});
_fetchData();
})
],
),
body: new Center(
child: _isLoading
? new CircularProgressIndicator()
: new ListView.builder(
itemCount: this.posts != null ? this.posts.length : 0,
itemBuilder: (context, i) {
final Post = this.posts[i];
final postDesc = Post["content"];
//All the below code is to fetch the image
var document = parse(postDesc);
//Regular expression
RegExp regExp = new RegExp(
r"(https?:\/\/.*\.(?:png|jpg|gif))",
caseSensitive: false,
multiLine: false,
);
final match = regExp
.stringMatch(document.outerHtml.toString())
.toString();
//print(document.outerHtml);
//print("firstMatch : " + match);
//Converting the regex output to image (Slashing) , since the output from regex was not perfect for me
if (match.length > 5) {
if (match.contains(".jpg")) {
imgUrl = match.substring(0, match.indexOf(".jpg"));
print(imgUrl);
} else {
imgUrl =
"https://pbs.twimg.com/profile_images/916384996092448768/PF1TSFOE_400x400.jpg";
}
}
String description = document.body.text.trim();
//print(description);
return new Container(
padding:
const EdgeInsets.fromLTRB(16.0, 16.0, 16.0, 8.0),
child: new Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
new Container(
width: 500.0,
height: 180.0,
decoration: new BoxDecoration(
shape: BoxShape.rectangle,
image: new DecorationImage(
fit: BoxFit.fill,
//check if the image is not null (length > 5) only then check imgUrl else display default img
image: new NetworkImage(imgUrl
.toString()
.length >
10
? imgUrl.toString()
: "https://pbs.twimg.com/profile_images/916384996092448768/PF1TSFOE_400x400.jpg")),
),
),
new Padding(
padding:
const EdgeInsets.symmetric(vertical: 10.0),
child: new Text(
Post["title"],
maxLines: 3,
style: new TextStyle(
fontSize: 18.0,
fontWeight: FontWeight.bold,
),
),
),
new Text(
description.replaceAll("\n", ", "),
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: new TextStyle(fontSize: 15.0),
),
new Padding(
padding:
const EdgeInsets.symmetric(vertical: 16.0),
child: new RaisedButton(
child: new Text("READ MORE",style: new TextStyle(color: Colors.white),),
color: Colors.blue,
onPressed: () {
//We will pass description to postview through an argument
Navigator
.of(context)
.push(new MaterialPageRoute<Null>(
builder: (BuildContext context) {
return PostView(Post['title'],description,imgUrl);
},
));
},
),
),
Divider(),
],
),
);
},
)));
}
}
Post_view.dart
import 'package:flutter/material.dart';
class PostView extends StatelessWidget {
var desc, title, image;
PostView(String title, String desc, String image) {
this.desc = desc;
this.title = title;
this.image = image;
}
#override
Widget build(BuildContext context) {
if (desc.toString().contains("\n\n\n\n")) {
desc = desc.toString().replaceAll("\n\n\n\n", "\n\n");
}
if (desc.toString().contains("\n\n\n")) {
desc = desc.toString().replaceAll("\n\n\n", "\n");
}
return new Scaffold(
appBar: new AppBar(
title: new Text("Blogger"),
),
body: new Container(
child: new SingleChildScrollView(
child: new Column(
children: <Widget>[
new Padding(
padding:
const EdgeInsets.symmetric(vertical: 16.0, horizontal: 16.0),
child: new Text(
title,
style: new TextStyle(
fontSize: 22.0,
fontWeight: FontWeight.bold,
),
),
),
new Padding(
padding:
const EdgeInsets.symmetric(vertical: 16.0, horizontal: 16.0),
child: new Container(
width: 500.0,
height: 180.0,
decoration: new BoxDecoration(
shape: BoxShape.rectangle,
image: new DecorationImage(
fit: BoxFit.fill,
//check if the image is not null (length > 5) only then check imgUrl else display default img
image: new NetworkImage(image.toString().length > 10
? image.toString()
: "https://pbs.twimg.com/profile_images/916384996092448768/PF1TSFOE_400x400.jpg")),
),
),
),
new Padding(
padding:
const EdgeInsets.symmetric(vertical: 16.0, horizontal: 16.0),
child: new Text(
desc,
style: new TextStyle(
fontSize: 18.0,
),
),
),
],
))),
);
}
}
The problem log reads:
main.dart: Name non-constant identifiers using lowerCamelCase.
post_view.dart: This class (or a class which this class inherits from) is marked as '#immutable', but one or more of its instance fields are not final: PostView.desc, PostView.title, PostView.image
my editor marks class post on the post_view.dart as immutable.. how do i fix that.
final url =
"https://www.googleapis.com/blogger/v3/blogs/MY_BLOG_ID_HERE/posts/?key=API_KEY_HERE";
get your blog ID and put it into the required space and also get an API key for the blog from blogger
it should look like this
final url = "https://www.googleapis.com/blogger/v3/blogs/409370562475495748/posts/?key=AIzaSyA3_4OfkfLc10vy5Q2WeUhfirGUUY4J78F8bk";
also try to make these final PostView.desc, PostView.title, PostView.image and see if it works
Related
I am new to flutter, I have built a quizz app that takes 5 questions randomly from a pool of questions and presents them to the user one after the other, then displays the total score at the end (on a different) screen with the option of retaking the quiz (with another set of randomly picked questions).
My issue I am facing is that when I choose to retake the quiz, if in the pool of questions presented there is a question from the past quiz, it still has its options highlighted (marked either wrong or correct as per the previous selection).
Can someone help me on how to totally dismiss previous choices after taking a quiz ?
This is an example of question answered in the previous quiz, and it came back with the option already highlighted (my previous answer).
[enter image description here][1]
[1]: https://i.stack.imgur.com/U1YFf.png[enter image description here][1]
Here is my code:
import 'package:flutter/material.dart';
import 'package:percent_indicator/percent_indicator.dart';
import 'package:schoolest_app/widgets/quizz/quizz_image_container.dart';
import '../../models/quizz.dart';
import '../../widgets/quizz/options_widget.dart';
import '../../widgets/quizz/quizz_border_container.dart';
import '../../widgets/quizz/result_page.dart';
class QuizzDisplayScreen extends StatefulWidget {
const QuizzDisplayScreen({
Key? key,
}) : super(key: key);
static const routeName = '/quizz-display';
#override
State<QuizzDisplayScreen> createState() => _QuizzDisplayScreenState();
}
class _QuizzDisplayScreenState extends State<QuizzDisplayScreen> {
enter code here
late String quizzCategoryTitle;
late List<Question> categoryQuestions;
late List<Question> quizCategoryQuestions;
var _loadedInitData = false;
#override
void didChangeDependencies() {
if (!_loadedInitData) {
final routeArgs =
ModalRoute.of(context)!.settings.arguments as Map<String, String>;
quizzCategoryTitle = (routeArgs['title']).toString();
// final categoryId = routeArgs['id'];
categoryQuestions = questions.where((question) {
return question.categories.contains(quizzCategoryTitle);
}).toList();
quizCategoryQuestions =
(categoryQuestions.toSet().toList()..shuffle()).take(5).toList();
_loadedInitData = true;
}
super.didChangeDependencies();
}
late PageController _controller;
int _questionNumber = 1;
int _score = 0;
int _totalQuestions = 0;
bool _isLocked = false;
void _resetQuiz() {
for (var element in quizCategoryQuestions) {
setState(()=> element.isLocked == false);
}
}
#override
void initState() {
super.initState();
_controller = PageController(initialPage: 0);
}
#override
void dispose() {
_controller.dispose();
_resetQuiz();
super.dispose();
}
#override
Widget build(BuildContext context) {
final myPrimaryColor = Theme.of(context).colorScheme.primary;
final mySecondaryColor = Theme.of(context).colorScheme.secondary;
double answeredPercentage =
(_questionNumber / quizCategoryQuestions.length);
return quizCategoryQuestions.isEmpty
? Scaffold(
appBar: AppBar(
title: Text(
'Quizz - $quizzCategoryTitle',
style: TextStyle(color: myPrimaryColor),
),
iconTheme: IconThemeData(
color: myPrimaryColor,
),
centerTitle: true,
backgroundColor: Colors.transparent,
elevation: 0,
flexibleSpace: Container(
decoration: BoxDecoration(`enter code here`
borderRadius: const BorderRadius.only(`enter code here`
bottomLeft: Radius.circular(15),
bottomRight: Radius.circular(15),
),
color: mySecondaryColor,
border: Border.all(color: myPrimaryColor, width: 1.0),
),
),
),
body: const Center(
child: Text('Cette catégorie est vide pour l\'instant'),
))
: Scaffold(
appBar: AppBar(
title: Text(
'Quizz - $quizzCategoryTitle',
style: TextStyle(color: myPrimaryColor),
),
iconTheme: IconThemeData(
color: myPrimaryColor,
),
centerTitle: true,
backgroundColor: Colors.transparent,
elevation: 0,
flexibleSpace: Container(
decoration: BoxDecoration(
borderRadius: const BorderRadius.only(
bottomLeft: Radius.circular(15),
bottomRight: Radius.circular(15),
),
color: mySecondaryColor,
border: Border.all(color: myPrimaryColor, width: 1.0),
),
),
),
body: Container(
// height: 600,
width: double.infinity,
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: Column(
children: [
const SizedBox(height: 10),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Text(
'Question $_questionNumber/${quizCategoryQuestions.length}',
style: const TextStyle(
fontSize: 20, fontWeight: FontWeight.bold),
),
CircularPercentIndicator(
radius: 40,
// animation: true,
// animationDuration: 2000,
percent: answeredPercentage,
progressColor: myPrimaryColor,
backgroundColor: Colors.cyan.shade100,
circularStrokeCap: CircularStrokeCap.round,
center: Text(
// ignore: unnecessary_brace_in_string_interps
'${(answeredPercentage * 100).round()} %',
style: const TextStyle(
fontSize: 10, fontWeight: FontWeight.bold),
),
// lineWidth: 10,
)
],
),
const SizedBox(height: 10),
Divider(
thickness: 1,
color: myPrimaryColor,
),
Expanded(
child: PageView.builder(
itemCount: quizCategoryQuestions.length,
controller: _controller,
physics: const NeverScrollableScrollPhysics(),
itemBuilder: (context, index) {
final _question = quizCategoryQuestions[index];
return buildQuestion(_question);
},
),
),
_isLocked
? buildElevatedButton(context)
: const SizedBox.shrink(),
const SizedBox(height: 10),
],
),
),
);
}
Column buildQuestion(Question question) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(height: 10),
question.text!.isNotEmpty
? QuizzBorderContainer(
childWidget: Text(
question.text!,
style: const TextStyle(
fontSize: 20, fontWeight: FontWeight.bold),
),
)
: const SizedBox.shrink(),
question.imagePath!.isNotEmpty
? QuizzImageContainer(imagePath: question.imagePath!)
: const SizedBox.shrink(),
Expanded(
child: OptionsWidget(
question: question,
onClickedOption: (option) {
if (question.isLocked) {
return;
} else {
setState(() {
question.isLocked = true;
question.selectedOption = option;
});
_isLocked = question.isLocked;
if (question.selectedOption!.isCorrect) {
_score++;
}
}
},
),
),
],
);
}
ElevatedButton buildElevatedButton(BuildContext context) {
final mySecondaryColor = Theme.of(context).colorScheme.secondary;
return ElevatedButton(
onPressed: () {
if (_questionNumber < quizCategoryQuestions.length) {
_controller.nextPage(
duration: const Duration(milliseconds: 1000),
curve: Curves.easeInExpo,
);
setState(() {
_questionNumber++;
_isLocked = false;
});
} else {
setState(() {
// _isLocked = false;
_totalQuestions = quizCategoryQuestions.length;
});
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (context) =>
ResultPage(score: _score, totalQuestions: _totalQuestions),
),
);
}
},
child: Text(
_questionNumber < quizCategoryQuestions.length
? 'Suivant'
: 'Voir le résultat',
style: TextStyle(
color: mySecondaryColor,
fontWeight: FontWeight.bold,
),
),
);
}
}
I don't seem to the solution to this.
And this is the code on the result page:
import 'package:flutter/material.dart';
import '../../screens/quizz/quizz_screen.dart';
class ResultPage extends StatefulWidget {
final int score;
final int totalQuestions;
const ResultPage({
Key? key,
required this.score,
required this.totalQuestions,
}) : super(key: key);
#override
State<ResultPage> createState() => _ResultPageState();
}
class _ResultPageState extends State<ResultPage> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: SizedBox(
height: 150,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Text(
'You got ${widget.score}/${widget.totalQuestions}',
style: const TextStyle(
fontSize: 30,
fontWeight: FontWeight.bold,
),
),
ElevatedButton(
onPressed: () {
Navigator.pop(context);
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (context) => const QuizzScreen(),
),
);
},
child: const Text('OK'),
),
],
),
),
),
);
}
}
I don't know what is missing to get the reset right.
When you want to take retest try to dispose all the answers which are saved in the memory. Or you can use these navigators to which might help you in solving the issue. Try using pushReplacement or pushAndRemoveUntil when navigating to retest, this will clear the memory of last pages and you will achive the goal which you want.
Im new to flutter hooks, and riverpod(state-manangement),
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
String _url = "https://owlbot.info/api/v4/dictionary/";
String _token = "ae7cbdfff57e548a4360348ee519123a741d8e3d";
TextEditingController _controller = TextEditingController();
StreamController _streamController;
Stream _stream;
Timer _debounce;
Future _search() async {
if (_controller.text == null || _controller.text.length == 0) {
_streamController.add(null);
return;
}
_streamController.add("waiting");
Response response = await get(_url + _controller.text.trim(),
headers: {"Authorization": "Token " + _token});
_streamController.add(json.decode(response.body));
}
#override
void initState() {
super.initState();
_streamController = StreamController();
_stream = _streamController.stream;
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Flictionary"),
bottom: PreferredSize(
preferredSize: Size.fromHeight(48.0),
child: Row(
children: <Widget>[
Expanded(
child: Container(
margin: const EdgeInsets.only(left: 12.0, bottom: 8.0),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(24.0),
),
child: TextFormField(
onChanged: (String text) {
if (_debounce?.isActive ?? false) _debounce.cancel();
_debounce = Timer(const Duration(milliseconds: 1000), () {
_search();
});
},
controller: _controller,
decoration: InputDecoration(
hintText: "Search for a word",
contentPadding: const EdgeInsets.only(left: 24.0),
border: InputBorder.none,
),
),
),
),
IconButton(
icon: Icon(
Icons.search,
color: Colors.white,
),
onPressed: () {
_search();
},
)
],
),
),
),
body: Container(
margin: const EdgeInsets.all(8.0),
child: StreamBuilder(
stream: _stream,
builder: (BuildContext ctx, AsyncSnapshot snapshot) {
if (snapshot.data == null) {
return Center(
child: Text("Enter a search word"),
);
}
if (snapshot.data == "waiting") {
return Center(
child: CircularProgressIndicator(),
);
}
return ListView.builder(
itemCount: snapshot.data["definitions"].length,
itemBuilder: (BuildContext context, int index) {
return ListBody(
children: <Widget>[
Container(
color: Colors.grey[300],
child: ListTile(
leading: snapshot.data["definitions"][index]
["image_url"] ==
null
? null
: CircleAvatar(
backgroundImage: NetworkImage(snapshot
.data["definitions"][index]["image_url"]),
),
title: Text(_controller.text.trim() +
"(" +
snapshot.data["definitions"][index]["type"] +
")"),
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
snapshot.data["definitions"][index]["definition"]),
)
],
);
},
);
},
),
),
);
}
}
i just wanted to convert above statefulWidget to HookWidget and how to use riverpod as statemanagenent for the above example. I know some basics of hooks and riverpod, but still i'm confused between the hooks, statemanagement(riverpod).
Please Can someone help understand them and provide some examples or at least convert the above code into the hook widget, and using hookbuilder
Thanks in advance
First off, the code:
final textProvider = StateProvider<String>((_) => '');
final responseFutureProvider =
FutureProvider.autoDispose.family<Response, String>((ref, text) async {
if (text == null || text.length == 0) {
throw Error();
}
final String _url = "https://owlbot.info/api/v4/dictionary/";
final String _token = "ae7cbdfff57e548a4360348ee519123a741d8e3d";
return await get(_url + text.trim(), headers: {"Authorization": "Token " + _token});
});
final responseProvider = Computed<AsyncValue<Response>>((read) {
final text = read(textProvider).state;
return read(responseFutureProvider(text));
});
String _useDebouncedSearch(TextEditingController controller) {
final search = useState(controller.text);
useEffect(() {
Timer? timer;
void listener() {
timer?.cancel();
timer = Timer(
const Duration(milliseconds: 1000),
() => search.value = controller.text,
);
}
controller.addListener(listener);
return () {
timer?.cancel();
controller.removeListener(listener);
};
}, [controller]);
return search.value;
}
class MyHomePage extends HookWidget {
const MyHomePage({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
final controller = useTextEditingController();
final text = useProvider(textProvider);
text.state = _useDebouncedSearch(controller);
return Scaffold(
appBar: AppBar(
title: Text("Flictionary"),
bottom: PreferredSize(
preferredSize: Size.fromHeight(48.0),
child: Row(
children: <Widget>[
Expanded(
child: Container(
margin: const EdgeInsets.only(left: 12.0, bottom: 8.0),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(24.0),
),
child: TextFormField(
controller: controller,
decoration: InputDecoration(
hintText: "Search for a word",
contentPadding: const EdgeInsets.only(left: 24.0),
border: InputBorder.none,
),
),
),
),
Icon(
Icons.search,
color: Colors.white,
),
],
),
),
),
body: MyHomePageBody(),
);
}
}
class MyHomePageBody extends HookWidget {
const MyHomePageBody({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
final text = useProvider(textProvider).state;
final response = useProvider(responseProvider);
response.when(
error: (err, stack) => Center(child: Text('Error: $err')),
loading: () => Center(child: CircularProgressIndicator()),
data: (response) => ListView.builder(
itemCount: Response["definitions"].length,
itemBuilder: (BuildContext context, int index) {
return ListBody(
children: <Widget>[
Container(
color: Colors.grey[300],
child: ListTile(
leading: response["definitions"][index]["image_url"] == null
? null
: CircleAvatar(
backgroundImage:
NetworkImage(response["definitions"][index]["image_url"]),
),
title: Text(text.trim() + "(" + response["definitions"][index]["type"] + ")"),
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(response["definitions"][index]["definition"]),
)
],
);
},
),
);
}
}
We add an external text provider so that we can read the text field from other providers.
We create a FutureProviderFamily so that we can perform the API call with a parameter, the text from your text field. In Riverpod, families enable the passing parameters to providers.
We create a Computed that will call the Future every time the value of the text provider changes. This returns an AsyncValue which is a wonderful replacement to the StreamBuilder you were using (will explain more).
Refactored your debounced search a bit to use the useEffect hook. This will handle disposing the resources for your timer and update the textProvider as necessary. (I learned this from Remi's Marvel example)
We no longer need an onChanged or manual button press to search, as the textprovider's state is being updated whenever the controller changes.
Moved the body of your page into its own class to separate what needs to be loaded from what is static.
Now, instead of a StreamBuilder, we can use AsyncValue to handle the loading, error, and success states of your build.
I know this was a lot to cover, so I'd recommend really digging into the docs to learn more about everything that's in this example.
I am developing a weather application with Flutter. And I am thinking of designing a background that will change according to the weather. But I don't know how. I saw similar questions, but I did not see any benefit.
Here is all of my code;
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:location/location.dart';
import 'package:flutter/services.dart';
import 'package:uygulama1/Weather.dart';
import 'package:uygulama1/WeatherItem.dart';
import 'package:uygulama1/WeatherData.dart';
import 'package:uygulama1/ForecastData.dart';
//PROJECT'S ROOT
void main() {
runApp(MaterialApp(
title: "WeatherApp",
home: MyApp(),
));
}
//PROJECTS MAIN CLASS
class MyApp extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return new MyAppState();
}
}
class MyAppState extends State<MyApp> {
bool isLoading = false;
WeatherData weatherData;
ForecastData forecastData;
Location _location = new Location();
String error;
#override
void initState() {
super.initState();
loadWeather();
}
Future<LocationData> getLocationData() async {
return await _location.getLocation();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Weather App',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
backgroundColor: Colors.tealAccent,
appBar: AppBar(
title: Text('Flutter Weather App'),
),
body: Center(
child: Column(mainAxisSize: MainAxisSize.min, children: <Widget>[
//BACKGROUND IMAGE
Container(
decoration: BoxDecoration(
image: new DecorationImage(
image: new AssetImage("assets/rain.jpg"),fit: BoxFit.cover
),
),
),
//END
Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Padding(
padding: const EdgeInsets.all(8.0),
child: weatherData != null
? Weather(weather: weatherData)
: Container(),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: isLoading
? CircularProgressIndicator(
strokeWidth: 2.0,
valueColor:
new AlwaysStoppedAnimation(Colors.black),
)
: IconButton(
icon: new Icon(Icons.refresh),
tooltip: 'Refresh',
onPressed: loadWeather,
color: Colors.black,
),
),
],
),
),
SafeArea(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
height: 200.0,
child: forecastData != null
? ListView.builder(
itemCount: forecastData.list.length,
scrollDirection: Axis.horizontal,
itemBuilder: (context, index) => WeatherItem(
weather: forecastData.list.elementAt(index)))
: Container(),
),
),
)
]))),
);
}
loadWeather() async {
setState(() {
isLoading = true;
});
LocationData location;
try {
location = await getLocationData();
error = null;
} on PlatformException catch (e) {
if (e.code == 'PERMISSION_DENIED') {
error = 'Permission denied';
} else if (e.code == 'PERMISSION_DENIED_NEVER_ASK') {
error =
'Permission denied - please ask the user to enable it from the app settings';
}
location = null;
}
if (location != null) {
final lat = location.latitude;
final lon = location.longitude;
final weatherResponse = await http.get(
'https://api.openweathermap.org/data/2.5/weather?APPID=d89de3f0b2dedfe4f923f1e7f709953a&lat=${lat.toString()}&lon=${lon.toString()}');
final forecastResponse = await http.get(
'https://api.openweathermap.org/data/2.5/forecast?APPID=d89de3f0b2dedfe4f923f1e7f709953a&lat=${lat.toString()}&lon=${lon.toString()}');
if (weatherResponse.statusCode == 200 &&
forecastResponse.statusCode == 200) {
return setState(() {
weatherData =
new WeatherData.fromJson(jsonDecode(weatherResponse.body));
forecastData =
new ForecastData.fromJson(jsonDecode(forecastResponse.body));
isLoading = false;
});
}
}
setState(() {
isLoading = false;
});
}
}
Weather.dart
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:uygulama1/WeatherData.dart';
class Weather extends StatelessWidget {
final WeatherData weather;
Weather({Key key, #required this.weather}) : super(key: key);
#override
Widget build(BuildContext context) {
var temperature = (weather.temp - 273.15).round();
return Column(
children: <Widget>[
Text(weather.name, style: new TextStyle(color: Colors.black)),
Text("\n" + weather.main,
style: new TextStyle(color: Colors.black, fontSize: 32.0)),
Text("Temp: " + '${temperature.toString()}°C',
style: new TextStyle(color: Colors.black)),
Image.network('https://openweathermap.org/img/w/${weather.icon}.png'),
Text("Date: " + new DateFormat.yMMMd().format(weather.date),
style: new TextStyle(color: Colors.black)),
Text("Hour: " + new DateFormat.Hm().format(weather.date),
style: new TextStyle(color: Colors.black)),
],
);
}
}
Other files actually not important.
Thanks for your helps.
You can have a map with keys of strings(type of weather) and values of AssetImages, e.g.
final Map<String, AssetImage> images = {"rain": AssetImage("assets/rain.jpg"),
"wind": AssetImage("assets/wind.jpg"),
"snow": AssetImage("assets/snow.jpg")};
For the background:
Container(
decoration: BoxDecoration(
image: new DecorationImage(
image: weatherData == null ? images["wind"] : images[weatherData.name],
fit: BoxFit.cover
),
),
),
Assumptions:
Default background before fetching the weatherData is AssetImage("assets/wind.jpg").
weatherData.name can be "wind", "snow", and "rain".
May be you can do like this if you have two condition:
Container(
decoration: BoxDecoration(
image: new DecorationImage(
image:
fit: BoxFit.cover,
(isRaining)? new AssetImage("assets/rain.jpg"):new AssetImage("assets/sunny.jpg")
),
),
If you have multiple conditions, you can do it like this:
Container(
decoration: BoxDecoration(
image: new DecorationImage(
image:
fit: BoxFit.cover,
if(isRaining)
new AssetImage("assets/rain.jpg")
else if(isSunny)
new AssetImage("assets/sunny.jpg")
else
new AssetImage("assets/cold.jpg")
),
),
Hope this helps! Happy Coding:)
I'm trying to open a PDF dynamically and not from a static string, so that I can upload multiple pdf's and it opens whichever the user has selected. It instantiates using the FullPDFViewerScreen widget below, and I'd like to be able to pass other PDF's and change the title also subjective to the PDF chosen.
Here is my class for it:
import 'data.dart';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:path_provider/path_provider.dart';
import 'package:flutter_full_pdf_viewer/full_pdf_viewer_scaffold.dart';
import 'dart:typed_data';
class Detail extends StatelessWidget {
final Book book;
Detail(this.book);
final String _documentPath = 'PDFs/test-en.pdf';
#override
Widget build(BuildContext context) {
Future<String> prepareTestPdf() async {
final ByteData bytes =
await DefaultAssetBundle.of(context).load(_documentPath);
final Uint8List list = bytes.buffer.asUint8List();
final tempDir = await getTemporaryDirectory();
final tempDocumentPath = '${tempDir.path}/$_documentPath';
final file = await File(tempDocumentPath).create(recursive: true);
file.writeAsBytesSync(list);
return tempDocumentPath;
}
//app bar
final appBar = AppBar(
elevation: .5,
title: Text(book.title),
);
///detail of book image and it's pages
final topLeft = Column(
children: <Widget>[
Padding(
padding: EdgeInsets.all(16.0),
child: Hero(
tag: book.title,
child: Material(
elevation: 15.0,
shadowColor: Colors.yellow.shade900,
child: Image(
image: AssetImage(book.image),
fit: BoxFit.cover,
),
),
),
),
text('${book.pages} pages', color: Colors.black38, size: 12)
],
);
///detail top right
final topRight = Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
text(book.title,
size: 16, isBold: true, padding: EdgeInsets.only(top: 16.0)),
text(
'by ${book.writer}',
color: Colors.black54,
size: 12,
padding: EdgeInsets.only(top: 8.0, bottom: 16.0),
),
Row(
children: <Widget>[
text(
book.price,
isBold: true,
padding: EdgeInsets.only(right: 8.0),
),
],
),
SizedBox(height: 32.0),
Material(
borderRadius: BorderRadius.circular(20.0),
shadowColor: Colors.blue.shade200,
elevation: 5.0,
child: MaterialButton(
onPressed: () {
// We need to prepare the test PDF, and then we can display the PDF.
prepareTestPdf().then(
(path) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => FullPdfViewerScreen(path)),
);
},
);
},
minWidth: 160.0,
color: Colors.blue,
child: text('Read Now', color: Colors.white, size: 13),
),
)
],
);
final topContent = Container(
color: Theme.of(context).primaryColor,
padding: EdgeInsets.only(bottom: 16.0),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Flexible(flex: 2, child: topLeft),
Flexible(flex: 3, child: topRight),
],
),
);
///scrolling text description
final bottomContent = Container(
height: 220.0,
child: SingleChildScrollView(
padding: EdgeInsets.all(16.0),
child: Text(
book.description,
style: TextStyle(fontSize: 13.0, height: 1.5),
),
),
);
return Scaffold(
appBar: appBar,
body: Column(
children: <Widget>[topContent, bottomContent],
),
);
}
//create text widget
text(String data,
{Color color = Colors.black87,
num size = 14,
EdgeInsetsGeometry padding = EdgeInsets.zero,
bool isBold = false}) =>
Padding(
padding: padding,
child: Text(
data,
style: TextStyle(
color: color,
fontSize: size.toDouble(),
fontWeight: isBold ? FontWeight.bold : FontWeight.normal),
),
);
}
class FullPdfViewerScreen extends StatelessWidget {
final String pdfPath;
FullPdfViewerScreen(this.pdfPath);
#override
Widget build(BuildContext context) {
return PDFViewerScaffold(
appBar: AppBar(
title: Text("Document"),
),
path: pdfPath);
}
}
main.dart where it generates the route for the detail page. I need to access the books.path that's where the pdf route is mentioned for each book.
generateRoute(RouteSettings settings) {
final path = settings.name.split('/');
final title = path[1];
Book book = books.firstWhere((it) => it.title == title);
return MaterialPageRoute(
settings: settings,
builder: (context) => Detail(book),
);
}
Update:
It seems, I misunderstood you question.
You just need to pass title and path as a parameter.
Change:
final Book book;
Detail(this.book);
final String _documentPath = 'PDFs/test-en.pdf';
To:
final Book book;
final String documentPath;
Detail(this.book, this.documentPath);
And you can get Title from book.title variable
If you want to get the pdf file from URL, you can use flutter_downloader widget.
Here is a working example.
You can pass the list of files with their file name and url.
Download them using:
task.taskId = await FlutterDownloader.enqueue(
url: task.link,
headers: {"auth": "test_for_sql_encoding"},
savedDir: _localPath,
showNotification: true,
openFileFromNotification: true);
And check if they are completed with:
if(item.task.status == DownloadTaskStatus.complete)
When completed, you can pass _localPath/fileName to FullPdfViewerScreen to open the file. (_localPath is the path you used while downloading the file.
You should also pass the path as parameter. Like this;
class Detail extends StatelessWidget {
final Book book;
final String documentPath; // <-- Add this line
Detail(this.book, this.documentPath); <-- Edit this line
#override
Widget build(BuildContext context) {
Future<String> prepareTestPdf() async {
final ByteData bytes =
await DefaultAssetBundle.of(context).load(documentPath); // Also edit your path
final Uint8List list = bytes.buffer.asUint8List();
final tempDir = await getTemporaryDirectory();
final tempDocumentPath = '${tempDir.path}/$documentPath';
final file = await File(tempDocumentPath).create(recursive: true);
file.writeAsBytesSync(list);
return tempDocumentPath;
}
...
On your generateRoute, you should do like this;
generateRoute(RouteSettings settings) {
final path = settings.name.split('/');
final title = path[1];
Book book = books.firstWhere((it) => it.title == title);
return MaterialPageRoute(
settings: settings,
builder: (context) => Detail(book,settings.name), // <-- This is your path as parameter.
);
}
I have a screen where users can add a location. Here, I have separated all my widgets into there own files as illustrated below;
import 'package:cached_network_image/cached_network_image.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'package:fluttershare/pages/location/location_help_screen.dart';
import 'package:fluttershare/widgets/common_widgets/customDivider.dart';
import 'package:uuid/uuid.dart';
import '../../widgets/camp_type_select.dart';
import '../../widgets/extra_location_notes.dart';
import '../../widgets/location_input.dart';
import '../../widgets/opening_times.dart';
import '../../widgets/post_media.dart';
import '../../widgets/space_avalibility.dart';
import '../../widgets/utility_type_select.dart';
import '../../widgets/width_restriction.dart';
import '../../widgets/height_restriction.dart';
import '../../models/locations.dart';
import '../../models/user.dart';
import '../home.dart';
class AddNewLocation extends StatefulWidget {
static const routeName = '/add-new-location';
final User currentUser;
AddNewLocation({this.currentUser});
_AddNewLocationState createState() => _AddNewLocationState();
}
class _AddNewLocationState extends State<AddNewLocation> {
String postId = Uuid().v4();
final _scaffoldKey = GlobalKey<ScaffoldState>();
PlaceLocation _pickedLocation;
int storyPostCount = 0;
bool isLoading = false;
void _selectPlace(double lat, double lng) {
_pickedLocation = PlaceLocation(lattitude: lat, longitude: lng);
}
getLocationPostCount() async {
setState(() {
isLoading = true;
});
QuerySnapshot snapshot = await locationPostRef
.document(currentUser.id)
.collection('user_location_posts')
.getDocuments();
setState(() {
storyPostCount = snapshot.documents.length;
});
}
createLocationPostInFirestore(
{String mediaUrl,
String description,
double heightRestriction,
double widthRestriction}) {
locationPostRef
.document(currentUser.id)
.collection("user_location_posts")
.document(postId)
.setData({
"postId": postId,
"ownerId": currentUser.id,
"username": currentUser.username,
"description": description,
"timestamp": timestamp,
"lattitude": _pickedLocation.lattitude,
"longitude": _pickedLocation.longitude,
"max_height": heightRestrictionValue.toStringAsFixed(0),
"max_width": widthRestrictionValue.toStringAsFixed(0),
});
}
handlePostSubmit() {
createLocationPostInFirestore(
heightRestriction: heightRestrictionValue,
widthRestriction: widthRestrictionValue,
);
SnackBar snackbar = SnackBar(
content: Text("Profile Updated"),
);
_scaffoldKey.currentState.showSnackBar(snackbar);
setState(() {
postId = Uuid().v4();
});
}
buildUploadUserHeader() {
return Container(
margin: EdgeInsets.only(bottom: 10),
height: 200,
child: Row(
children: <Widget>[
Expanded(
flex: 2,
child: Container(
color: Colors.blue,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
ListTile(
leading: CircleAvatar(
backgroundImage:
CachedNetworkImageProvider(currentUser.photoUrl)),
),
],
),
),
),
Expanded(
flex: 6,
child: Container(
color: Colors.pink,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Text(currentUser.displayName),
],
),
),
),
],
),
);
}
buildCampUploadForm() {
return Container(
child: SingleChildScrollView(
child: Column(
children: <Widget>[
//buildUploadUserHeader(), //TODO: This is the profile header that is dissabled for now. Work on possibly a header in the future.
Container(
padding: EdgeInsets.all(15),
child: Column(
children: <Widget>[
CampTypeSelect(),
CustomDivider(),
LocationInput(_selectPlace),
CustomDivider(),
HeightRestriction(),
WidthRestriction(),
SpaceAvalibility(),
OpeningTimes(),
CustomDivider(),
PostMedia(),
CustomDivider(),
UtilityServices(),
CustomDivider(),
ExtraLocationNotes(),
Container(
height: 80,
margin: EdgeInsets.only(top: 10, bottom: 10),
child: Row(
children: <Widget>[
Expanded(
child: FlatButton(
color: Colors.black,
onPressed: () => handlePostSubmit(),
child: Text(
"SUBMIT",
style: Theme.of(context).textTheme.display2,
),
padding: EdgeInsets.all(20),
),
)
],
),
),
],
),
),
],
),
));
}
#override
Widget build(BuildContext context) {
return Scaffold(
key: _scaffoldKey,
appBar: AppBar(
automaticallyImplyLeading: false,
title: const Text(
'Add New Location',
style: TextStyle(color: Colors.black),
),
actions: <Widget>[
// action button
IconButton(
icon: Icon(Icons.info_outline),
color: Colors.black,
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
fullscreenDialog: true,
builder: (context) => LocationSubmitHelpScreen()),
);
},
),
// action button
IconButton(
icon: Icon(Icons.close),
color: Colors.black,
onPressed: () {
Navigator.of(context).pop();
},
),
],
),
body: buildCampUploadForm(),
backgroundColor: Colors.white,
);
}
}
What I am trying to do is pass the data back from the widget ExtraLocationNotes()
to the function createLocationPostInFirestore().
For context, this is what my widget looks like;
import 'package:flutter/material.dart';
import 'common_widgets/custom_form_card.dart';
class ExtraLocationNotes extends StatefulWidget {
_ExtraLocationNotesState createState() => _ExtraLocationNotesState();
}
class _ExtraLocationNotesState extends State<ExtraLocationNotes> {
TextEditingController descriptionController = TextEditingController();
#override
Widget build(BuildContext context) {
return CustomFormCard(
child: Column(
children: <Widget>[
Container(
child: Row(
children: <Widget>[
Text(
"EXTRA INFORMATION",
style: TextStyle(
fontSize: 18.0,
color: Colors.black,
fontWeight: FontWeight.w400,
letterSpacing: 2.0,
),
),
],
),
),
SizedBox(height: 20),
TextFormField(
controller: descriptionController,
maxLines: 6,
maxLength: 250,
maxLengthEnforced: true,
style:
new TextStyle(fontSize: 18.0, height: 1.3, color: Colors.black),
decoration: const InputDecoration(
hintText:
"Please write a description of this location for fellow travellers.",
alignLabelWithHint: true,
border: OutlineInputBorder(
borderRadius: BorderRadius.only(),
borderSide: BorderSide(color: Colors.black),
),
),
),
],
),
);
}
}
How do I pass the data back to the parent widget?
You need a callback, which will be triggered in the child widget then the value will be updated in the parent widget:
// 1- Define a pointers to executable code in memory, which is the callback.
typedef void MyCallback(String val);
class ExtraLocationNotes extends StatefulWidget {
// 2- You will pass it to this widget with the constructor.
final MyCallback cb;
// 3- ..pass it to this widget with the constructor
ExtraLocationNotes({this.cb});
_ExtraLocationNotesState createState() => _ExtraLocationNotesState();
}
class _ExtraLocationNotesState extends State<ExtraLocationNotes> {
//..
//...
RaisedButton(
//..
// 4- in any event inside the child you can call the callback with
// the data you want to send back to the parent widget:
onPressed: () {
widget.cb("Hello from the other side!");
}
),
}
Then inside the parent widget you need to catch the data which sent form the child:
class AddNewLocation extends StatefulWidget {
//...
_AddNewLocationState createState() => _AddNewLocationState();
}
class _AddNewLocationState extends State<AddNewLocation> {
// 1- Global var to store the data that we're waiting for.
String _dataFromMyChild = "";
buildCampUploadForm() {
return Container(
//...
//...
// 2- Pass the callback with the constructor of the child, this
// will update _dataFromMyChild's value:
ExtraLocationNotes(cb: (v) => setState(() => _dataFromMyChild = v)),
//..
}
// then
createLocationPostInFirestore() {
// Use _dataFromMyChild's value here
}
}
You can use the BuildContext object to get the context widget (might no be the parent!) couldn't read it all but as i understand that you need to pass the info from the child to the parent ,and you can do it with some like this :-
(context.widget as MyType).doStuff();
Note.
please check first with
print(context.widget.runtimeType);
but to make a better solution make a mutable data object that is passed from parent to the child so when changes happens it reflect's on the parent so you can separate business logic from ui logic.