When I run main.dart on my real phone code runs without any mistakes, I can see the string value exactly on my Location page but when I adjust my android emulator phone's location Turkey/İstanbul it stuckes on spinner (Loading page), Spinner keeps turning forever so Location page never open, by the way "I/eatherforecast(19501): Waiting for a blocking GC ProfileSaver" error written on console. I really wonder the reason of this tedious issue. Have a nice day..
import 'package:flutter/material.dart';
import 'package:weatherforecast2/loadingpage.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData.dark().copyWith(),
home: LoadingPage(),
);
}
}
import 'package:flutter/material.dart';
import 'package:flutter_spinkit/flutter_spinkit.dart';
import 'package:weatherforecast2/locationpage.dart';
import 'locationfinder.dart';
import 'package:geolocator/geolocator.dart';
import 'network.dart';
class LoadingPage extends StatefulWidget {
static String id = "loadingpage";
#override
_LoadingPageState createState() => _LoadingPageState();
}
class _LoadingPageState extends State<LoadingPage> {
Map<String,dynamic> currentLocationWeatherData;
void initState(){
super.initState();
getDecodedCurrentLocationWeatherData();
}
void getDecodedCurrentLocationWeatherData()async{
Position position=await LocationFinder().getCurrentLocation();
currentLocationWeatherData = await NetworkHelper().getCurrentLocationWeather(position.latitude,
position.longitude);
Navigator.push(context, MaterialPageRoute(builder: (context) {
return LocationPage(
currentLocationWeatherData: currentLocationWeatherData,
);
}));
}
#override
Widget build(BuildContext context) {
return Container(
child: Center(
child:SpinKitFadingCube(
color: Colors.white,
size: 50,
),
),
);
}
}
import 'dart:convert';
import 'package:http/http.dart';
const String appid="57aad03f4e48ca815bb1184e74624f46";
const String openWeatherMapURL="https://api.openweathermap.org/data/2.5/weather";
class NetworkHelper{
Future<dynamic>getCurrentLocationWeather(lat,lon)async{
Response response=await get("https://api.openweathermap.org/data/2.5/weather?
lat=$lat&lon=$lon&appid=$appid");
if (response.statusCode == 200){
return jsonDecode(response.body);
}
}
}
import 'package:flutter/material.dart';
class LocationPage extends StatefulWidget {
LocationPage({#required this.currentLocationWeatherData});
final Map<String,dynamic> currentLocationWeatherData;
static String id = "locationpage";
#override
_LocationPageState createState() => _LocationPageState();
}
class _LocationPageState extends State<LocationPage> {
#override
Widget build(BuildContext context) {
return Container(
color: Colors.white,
child: Center(
child: Text(
widget.currentLocationWeatherData["weather"][0]["description"],
),
),
);
}
}
import 'package:geolocator/geolocator.dart';
class LocationFinder{
Future<Position> getCurrentLocation()async{
Position position = await Geolocator.getCurrentPosition(desiredAccuracy: LocationAccuracy.low);
return position;
}
}
Related
Isar does not persist state, every time I close my mobile application and open it again, previous values are not there, when they should be there. I'm fetching those values from Isar.
Please show with a counter app example how this works.
I'm attaching files.
main.dart
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:isarapp/counter_schema.dart';
import 'package:isar/isar.dart';
import 'package:isarapp/home_screen.dart';
import 'package:path_provider/path_provider.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
var directory = await getApplicationDocumentsDirectory();
var path = directory.path;
await Isar.open(
[CounterObjectSchema],
directory: path,
inspector: true,
name: "isardb",
);
runApp(const MyApp());
}
class MyApp extends ConsumerWidget {
const MyApp({super.key});
#override
Widget build(BuildContext context, WidgetRef ref) {
return ProviderScope(
child: MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const HomeScreen(),
),
);
}
}
I have edited android manifest file to get store permission then using permission_handler for getting storage access.
home_screen.dart
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:isar/isar.dart';
import 'package:isarapp/counter_schema.dart';
import 'package:permission_handler/permission_handler.dart';
final counterProvider1 = StateProvider<int>((ref) {
return 0;
});
class HomeScreen extends ConsumerStatefulWidget {
const HomeScreen({super.key});
#override
ConsumerState<ConsumerStatefulWidget> createState() => _HomeScreenState();
}
getStoragePermission() async {
var status = await Permission.storage.status;
if (status.isDenied) {
Permission.storage.request();
}
}
class _HomeScreenState extends ConsumerState<HomeScreen> {
Isar? isar = Isar.getInstance("isardb");
CounterObject counterObject = CounterObject(counter: 9);
initCounter() async {
var count = await isar?.collection<CounterObject>().get(counterObject.id);
//reset counter on launch
ref.read(counterProvider1.notifier).state = count?.counter ?? -1;
}
increment() async {
counterObject.counter = counterObject.counter + 1;
//update state
ref.read(counterProvider1.notifier).state = counterObject.counter;
//writing counterValue to isardb on every increment call
isar?.writeTxn(
() async => await isar?.collection<CounterObject>().put(counterObject),
);
}//increment
#override
void initState() {
getStoragePermission();
//putting counterObject in
isar?.writeTxn(
() async => await isar?.counterObjects.put(counterObject),
);
initCounter();
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Center(
child: Column(
children: [
Text(
ref.watch(counterProvider1).toString(),
style: const TextStyle(fontSize: 54),
),
GestureDetector(
onTap: () => increment(),
child: Container(
width: 200,
height: 40,
color: Colors.blue[300],
child: const Center(child: Text("Add")),
),
),
],
),
),
),
);
}
}
CounterObjectSchema
import 'package:isar/isar.dart';
part 'counter_schema.g.dart';
#collection
class CounterObject {
Id id = Isar.autoIncrement;
int counter;
CounterObject({required this.counter});
}
in my app i want to detect in the splashscreen if this app is started for the first time.
For that i want to use the hive nosql package.
After that if the app is started for the first time it will open the welcome page and if not the login page.
main.dart
import 'package:flutter_config/flutter_config.dart';
import 'package:flutter/material.dart';
import 'package:app/pages/splash/splash_page.dart';
import 'package:hive/hive.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'config/theme/theme.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await FlutterConfig.loadEnvVariables();
await Hive.initFlutter();
await Hive.openBox('settings');
runApp(const App());
}
class App extends StatefulWidget {
const App({Key? key}) : super(key: key);
#override
State<App> createState() => _AppState();
}
class _AppState extends State<App> {
#override
void dispose() async {
Hive.close();
super.dispose();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'App',
debugShowCheckedModeBanner: false,
theme: lightThemeData(context),
darkTheme: darkThemeData(context),
home: const SplashPage(),
);
}
}
splash_page.dart
import 'package:flutter/material.dart';
import 'package:app/pages/login/login_page.dart';
import 'package:app/pages/welcome/welchome_page.dart';
import 'package:app/services/settings_service.dart';
class SplashPage extends StatefulWidget {
const SplashPage({Key? key}) : super(key: key);
#override
State<SplashPage> createState() => _SplashPageState();
}
class _SplashPageState extends State<SplashPage> {
#override
Widget build(BuildContext context) {
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (_) =>
Settings().isFirstTime ? const WelcomePage() : const LoginPage(),
),
);
return const Scaffold(
body: Center(
child: SizedBox(
width: 125,
height: 125,
child: Icon(Icons.clear),
),
),
);
}
}
there i call the function "var _isFirstTime = Settings().isFirstTime;" it should return me a bool
settings_service.dart
import 'package:hive/hive.dart';
import 'package:hive_flutter/hive_flutter.dart';
class Settings {
final Box _settingsStorage = Hive.box('settings');
get isFirstTime {
if (_settingsStorage.get('firstRun')) {
return true;
} else {
_settingsStorage.put('firstRun', true);
}
return false;
}
}
i got this error:
════════ Exception caught by widgets library ═══════════════════════════════════
The following _TypeError was thrown building SplashPage(dirty, state: _SplashPageState#261ae):
type 'Null' is not a subtype of type 'bool'
how can i solve this? later i would like to use the settings service for other settings as well ...
In the setting_service.dart,
I think this line -> _settingsStorage.get('firstRun'), is returning null. From what I understand what you should do is that whenever you get firstRun as null, you should assign it true.
if (_settingsStorage.get('firstRun') ?? true) {
return _settingsStorage.get('firstRun') ?? true;
}
I am new to Flutter, and bloc too. I got the idea, how bloc works. But When I create a simple app as the first step of my note app. The bloc doesn't give the data. This simple app has two screens. list screen and Notedetailscreen. Button in NoteDetailScreen tapped, data does not print to the text widget.
main.dart
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:note_demo_bloc/bloc/note_bloc.dart';
import 'package:note_demo_bloc/list_screen.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return BlocProvider<NoteBloc>(
create: (context) => NoteBloc(),
child: MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: ListScreen(),
),
);
}
}
note_bloc.dart
import 'dart:async';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:meta/meta.dart';
part 'note_event.dart';
part 'note_state.dart';
class NoteBloc extends Bloc<NoteblocEvent, NoteblocState> {
NoteBloc() : super(NoteblocInitial());
#override
Stream<NoteblocState> mapEventToState(
NoteblocEvent event,
) async* {
if (event == NoteSaveEvent) {
yield NoteSaveState(state);
}
}
}
part of 'note_bloc.dart';
#immutable
abstract class NoteblocEvent {}
class NoteSaveEvent extends NoteblocEvent {
NoteSaveEvent(this.text);
final text;
}
note_state.dart
part of 'note_bloc.dart';
#immutable
abstract class NoteblocState {}
class NoteblocInitial extends NoteblocState {}
class NoteSaveState extends NoteblocState {
NoteSaveState(this.text);
final text;
}
list_screen.dart
import 'package:flutter/material.dart';
import 'package:note_demo_bloc/note_detail_screen.dart';
class ListScreen extends StatefulWidget {
const ListScreen({Key? key}) : super(key: key);
#override
_ListScreenState createState() => _ListScreenState();
}
class _ListScreenState extends State<ListScreen> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Text('hi'),
floatingActionButton: FloatingActionButton(
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => NoteDetailScreen(),
),
);
},
),
);
}
}
Note_detailscreen.dart
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:note_demo_bloc/bloc/note_bloc.dart';
class NoteDetailScreen extends StatefulWidget {
const NoteDetailScreen({Key? key}) : super(key: key);
#override
_NoteDetailScreenState createState() => _NoteDetailScreenState();
}
class _NoteDetailScreenState extends State<NoteDetailScreen> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
ElevatedButton(
onPressed: () {
BlocProvider.of<NoteBloc>(context).add(NoteSaveEvent('hi'));
},
child: Text('click'),
),
BlocBuilder<NoteBloc, NoteblocState>(
builder: (context, state) {
return Text(state.toString());
},
)
],
),
);
}
}
Your bloc, state, and event looks fine. When you push screen you might need to use BlocProvider again. So try this:
main.dart
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:note_demo_bloc/bloc/note_bloc.dart';
import 'package:note_demo_bloc/list_screen.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
NoteBloc _noteBloc = NoteBloc();
#override
Widget build(BuildContext context) {
return BlocProvider<NoteBloc>(
create: (context) => _noteBloc(),
child: MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: ListScreen(),
),
);
}
}
list_screen.dart
import 'package:flutter/material.dart';
import 'package:note_demo_bloc/note_detail_screen.dart';
class ListScreen extends StatefulWidget {
const ListScreen({Key? key}) : super(key: key);
#override
_ListScreenState createState() => _ListScreenState();
}
class _ListScreenState extends State<ListScreen> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Text('hi'),
floatingActionButton: FloatingActionButton(
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => BlocProvider.value(value: BlocProvider.of<NoteBloc>(context), child: NoteDetailScreen()),
),
);
},
),
);
}
}
Note_detailscreen.dart
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:note_demo_bloc/bloc/note_bloc.dart';
class NoteDetailScreen extends StatefulWidget {
const NoteDetailScreen({Key? key}) : super(key: key);
#override
_NoteDetailScreenState createState() => _NoteDetailScreenState();
}
class _NoteDetailScreenState extends State<NoteDetailScreen> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
ElevatedButton(
onPressed: () {
BlocProvider.of<NoteBloc>(context).add(NoteSaveEvent('hi'));
},
child: Text('click'),
),
BlocBuilder<NoteBloc, NoteblocState>(
bloc: BlocProvider.of<NoteBloc>(context),
builder: (context, state) {
return Text(state.toString());
},
)
],
),
);
}
}
So, this is not an answer of your question but consider that as alternative (for future users of SO).
As state management is a free choice, and everyone could manage that as it’s “modus operandi“ this helper class “home made” could be a good choice.
import 'dart:async';
import 'dart:core';
class Method {
Method(this.name, this.params);
final String name;
final Map<String, Object> params;
}
class _Controller {
_Controller._();
static final Map<String, _Controller> _this = new Map<String, _Controller>();
final Map<String, Function(Method)> _funcs = new Map<String, Function(Method)>();
factory _Controller(String identifier) => _this.putIfAbsent(identifier, () => _Controller._());
Future<void> activateListener(String listenerId, Function(Method) function) async {
if (function != null)
_funcs.containsKey(listenerId) ? _funcs[listenerId] = function : _funcs.putIfAbsent(listenerId, () => function);
}
Future<void> deactivateListener(String listenerId) async =>
_funcs.removeWhere((String key, Function(Method) func) => key == listenerId);
Future<void> removeListener(String identifier) async =>
_this.removeWhere((String key, _Controller mClass) => key == identifier);
Future<void> callMethod(String methodName, {Map<String, Object> params}) async =>
Future.forEach(_funcs.values.where((v) => v != null), (func) async => func.call(Method(methodName, params)));
}
mixin MethodListener on Object {
_Controller _getController(String identifier) => _Controller(identifier ?? this.runtimeType.toString());
Future<void> activateListener({String identifier, List<String> identifiers}) async {
if (identifiers != null && identifiers.length > 0)
identifiers.forEach(
(String currentId) => _getController(currentId).activateListener(this.hashCode.toString(), onMethodListener));
else
_getController(identifier).activateListener(this.hashCode.toString(), onMethodListener);
}
Future<void> deactivateListener({String identifier, List<String> identifiers}) async {
if (identifiers != null && identifiers.length > 0)
identifiers.forEach((String currentId) => _getController(currentId).deactivateListener(this.hashCode.toString()));
else
_getController(identifier).deactivateListener(this.hashCode.toString());
}
Future<void> removeListener({String identifier}) async => _getController(identifier).removeListener(identifier);
void onMethodListener(Method method) async => null;
Future<void> callMethodOn(String identifier, String methodName, {Map<String, Object> params}) async =>
_getController(identifier).callMethod(methodName, params: params);
}
class MethodManager with MethodListener {
MethodManager._();
static MethodManager _this;
factory MethodManager() {
if (_this == null) _this = MethodManager._();
return _this;
}
Future<void> callMethodOnWidgets(List<String> identifiers, String methodName, {Map<String, Object> params}) async =>
identifiers.forEach((String currentId) => callMethodOn(currentId, methodName, params: params));
#override
Future<void> callMethodOn(String identifier, String methodName, {Map<String, Object> params}) async =>
super.callMethodOn(identifier, methodName, params: params);
}
then you can implements classes with “with MethodListener” as follows:
import 'package:flutter/material.dart';
import 'package:yourpackagehere/utils/XMethods.dart';
class Test extends StatefulWidget {
static const String NAME = "Test";
#override
createState() => _TestState();
}
class _TestState extends State<Test> with MethodListener {
String _ciao;
#override
void initState() {
super.initState();
this.activateListener(identifier: Test.NAME);
}
#override
void dispose() {
this.deactivateListener(identifier: Test.NAME);
super.dispose();
}
#override
Widget build(BuildContext context) {
return Container(child: Text(_ciao));
}
#override
void onMethodListener(Method method) {
switch (method.name) {
case "say_hello":
if (mounted) {
setState(() {
_ciao = method.params["my_string"];
});
}
break;
}
}
}
Usage:
From everywhere (from widgets or classes):
MethodManager().callMethodOn(Test.NAME, "say_hello", params: {"my_string": "SIAMO CAMPIONI DI EUROPA!!!"});
I made a function to fetch data from json file and I show that data to one page when ever my fetch function run it show an erorr for the time till Json fetch that is 3 to 4 second after that data fetch and show succesfully but that error show on screen is very awkward.
import 'dart:convert';
import 'package:flutter/services.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
void main() => runApp(News1());
class News1 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: "Flutter",
debugShowCheckedModeBanner: false,
home: HomePage(),
);
}
}
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
List data = [];
#override
void initState() {
fetchData();
super.initState();
}
void fetchData() async {
final response = await http.get('jsonfilelinkhere');
if (response.statusCode == 200) {
setState(() {
data = json.decode(response.body);
});
}
}
#override
Widget build(BuildContext context) {
SystemChrome.setPreferredOrientations(
[DeviceOrientation.landscapeLeft, DeviceOrientation.landscapeRight]);
return Scaffold(
body: Padding(
padding: EdgeInsets.only(left: 50 ,right: 50),
child:ListView(
children: <Widget>[
Center(
child: Text(data[3]['Head']),
),
Center(
child: Text(data[0]['Description']),
),
Image.network(data[0]['ImgUrl']),
],
),
)
);
}
}
hope you got your answer. In case you can make a check that while your array is equal to null show CircularProgressIndicator(), else show data. If you are unable to do so I can share the code for you.
your fetchData() function is asynchronous, so your app tap the back of your function saying "hi, fetchData() start to work!!" but your app goes on minding its own job.
And you gave this job for it:
child: Text(data[3]['Head']),
so your app will hit this line of code while your data variable still is an empty list.
You have to prepare it for this situation. You can prepare the default value of the data or you can check if it's empty in the Widgets that depends on it.
You encountered that error as you displayed that data before it could actually load.
Use FutureBuilder to solve your issue.
Example code:
import 'dart:convert';
import 'package:flutter/services.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
void main() => runApp(News1());
class News1 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: "Flutter",
debugShowCheckedModeBanner: false,
home: HomePage(),
);
}
}
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
List data = [];
#override
void initState() {
fetchData();
super.initState();
}
Future<Map<String, dynamic>> fetchData() async {
final response = await http.get('jsonfilelinkhere');
if (response.statusCode == 200) {
setState(() {
return json.decode(response.body);
});
}
}
#override
Widget build(BuildContext context) {
SystemChrome.setPreferredOrientations(
[DeviceOrientation.landscapeLeft, DeviceOrientation.landscapeRight]);
return Scaffold(
body: Padding(
padding: EdgeInsets.only(left: 50 ,right: 50),
child:FutureBuilder<Map<String, dynamic>>(
future: fetchData, // async work
builder: (context,snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.waiting: return new Center(child: Text('Loading....'));
default:
if (snapshot.hasError)
return Text("Error!");
else{
data = snapshot.data;
return ListView(
children: <Widget>[
Center(
child: Text(data[3]['Head']),
),
Center(
child: Text(data[0]['Description']),
),
Image.network(data[0]['ImgUrl']),
],
)}
}
},
),
)
);
}
}
I am learning Flutter. I wrote small app to getting key from API and print it on screen. The problem is that my getApiKey() method is looping.
Why? And How I can prevent it?
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
import 'package:provider/provider.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: ChangeNotifierProvider<TenderApiData>(
builder: (_) => TenderApiData(), child: HomePage()),
);
}
}
class HomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(appBar: AppBar(), body: MyContainer());
}
}
class MyContainer extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Column(
children: <Widget>[MyTestWidget()],
);
}
}
class TenderApiData with ChangeNotifier {
String access_token;
String url = "https://";
getApiKey() async
{
var response = await http.post(url, headers: {"Accept": "application/json"});
// await Future.delayed(Duration(seconds: 25));
if (response.statusCode == 200)
{
access_token = json.decode(response.body)['access_token'];
notifyListeners();
}
}
}
class MyTestWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
Provider.of<TenderApiData>(context).getApiKey();
var result = Provider.of<TenderApiData>(context).access_token;
return Row(
children: <Widget>[
Flexible(child: Text("Data: $result"))
],
);
}
}
The reason this happens is because you are notifying listeners in your getApiKey function and then calling getApiKey in your build method. The build method is called when you notify your listeners, see why this loops?
Anyways, to prevent it, you simply convert your StatelessWidget to a StatefulWidget and only call getApiKey in State.didChangeDependencies (not in initState because you need access to the BuildContext):
class MyTestWidget extends StatefulWidget {
#override
_MyTestWidgetState createState() => _MyTestWidgetState();
}
class _MyTestWidgetState extends State<MyTestWidget> {
bool apiKeyLoaded;
#override
void initState() {
apiKeyLoaded = false;
super.initState();
}
#override
void didChangeDependencies() {
if (!apiKeyLoaded) {
Provider.of<TenderApiData>(context).getApiKey();
apiKeyLoaded = true;
}
super.didChangeDependencies();
}
#override
Widget build(BuildContext context) {
var result = Provider
.of<TenderApiData>(context)
.access_token;
return Row(
children: <Widget>[
Flexible(child: Text("Data: $result"))
],
);
}
}