I am trying to create a web blog with Hacker News API, but every time I try to run, I get this confusing error thrown at me and I do not get it. asked chatgpt, it gave me a solution but I still run into the same error. I am pasting all necessary classes.
first this is my database class.
import 'dart:io';
import 'package:new_flutter/src/models/item_model.dart';
import 'package:path/path.dart';
import 'package:path_provider/path_provider.dart';
import 'package:sqflite/sqflite.dart';
final newsDbProv2 = NewsDbProv2();
class NewsDbProv2 {
static const _dbName = "items.db";
static const _dbVersion = 1;
static const _tableName = "Items";
Database? db;
/// INITIALIZING THE DATABASE......
Future<void> init() async {
// if (db != null) return;
if (db == null) {
try {
Directory documentsDirectory = await getApplicationDocumentsDirectory();
final path = join(documentsDirectory.path, _dbName);
db = await openDatabase(path,
version: _dbVersion, onCreate: createTable);
} catch (e) {
print('Error initializing database: $e');
}
}
}
///CREATING THE DATABASE TABLE........
Future<void> createTable(Database newDb, int version) async {
try {
await newDb.execute("""
CREATE TABLE $_tableName
(
id INTEGER PRIMARY KEY,
type TEXT,
by TEXT,
time INTEGER,
text TEXT,
parent INTEGER,
kids BLOB,
dead INTEGER,
deleted INTEGER,
url TEXT,
score INTEGER,
title TEXT,
descendants INTEGER
)
""");
} catch (e) {
print('Error creating table: $e');
}
}
///Fetching an ITEM by ID from the DATABASE.....
Future<ItemModel?> fetchItem(int id) async {
try {
if (db == null) await init();
final maps = await db!.query(
_tableName,
columns: null,
where: "id = ?",
whereArgs: [id],
);
if (maps.isNotEmpty) {
return ItemModel.fromDB(maps.first);
}
} catch (e) {
print('Error fetching item from the database: $e');
}
return null;
}
Future<int> addItem(ItemModel item) async {
try {
if (db == null) await init();
return await db!.transaction((txn) async {
return await txn.insert(
// return db!.insert(
_tableName,
item.toMapForDb(),
conflictAlgorithm: ConflictAlgorithm.ignore,
);
});
} catch (e) {
print('Error adding item to database: $e');
return 0;
}
}
// Future<int> addItem(ItemModel item) async {
// return await db!.transaction((txn) async {
// return await txn.insert(
// // return db!.insert(
// _tableName,
// item.toMapForDb(),
// conflictAlgorithm: ConflictAlgorithm.ignore,
// );
// });
// }
}
// Future<List<int>> fetchTopIds() {
// // TODO: implement fetchTopIds
// throw UnimplementedError();
// }
this is the api class
import 'dart:convert';
import 'package:http/http.dart' show Client;
import 'package:new_flutter/src/models/item_model.dart';
const rootDomain = '';
class NewsApiProv2 {
Client client = Client();
/// Fetching Top story IDs from the API.......
Future<List<int>> fetchTopIds() async {
try {
final response = await client
.get(Uri.parse('$rootDomain/topstories.json'))
.timeout(const Duration(seconds: 10));
if (response.statusCode == 200) {
final ids = json.decode(response.body);
return ids.cast<int>();
} else {
print("Error: Failed to fetch top IDs. Status: ${response.statusCode}");
}
} catch (e) {
print('Exception during fetchTopIds: $e');
}
return [];
}
/// Fetching a specific Story/ITEM by ID........
Future<ItemModel?> fetchItem(int id) async {
try {
final response = await client
.get(Uri.parse('$rootDomain/item/$id.json'))
.timeout(const Duration(seconds: 10));
if (response.statusCode == 200) {
final parsedJson = json.decode(response.body);
if (parsedJson == null || parsedJson.isEmpty) {
print('Empty or null response for item with id $id');
return null;
}
return ItemModel.fromJson(parsedJson);
} else {
print(
'Failed to fetch item with id $id, Status Code: ${response.statusCode}');
throw Exception('Failed to load item');
}
} catch (e) {
print('Exception during fetchItem: $e');
}
return null;
}
}
This is repository class:
import 'package:new_flutter/src/models/item_model.dart';
import 'package:new_flutter/src/resources/new_resource_dir/news_api_prov2.dart';
import 'package:new_flutter/src/resources/new_resource_dir/news_db_prov_2.dart';
class RepositoryProv2 {
/// Initialize instances of the providers
final NewsApiProv2 _newsApiProvider = NewsApiProv2();
final NewsDbProv2 _newsDbProvider = NewsDbProv2();
/// Fetch the top story IDs from the API
Future<List<int>> fetchTopIds() async {
try {
return await _newsApiProvider.fetchTopIds();
} catch (e) {
print("Error fetching top IDs: $e");
return [];
}
}
/// Fetch an individual item from either API or Database (cache)
Future<ItemModel?> fetchItems(int id) async {
try {
/// First try fetching the item from the database (cache)
ItemModel? item = await _newsDbProvider.fetchItem(id);
/// If the item is not found in the database, fetch from the API
if (item == null) {
item = await _newsApiProvider.fetchItem(id);
/// If the item was found, save it in the database (cache)
if (item != null) {
await _newsDbProvider.addItem(item);
}
}
return item;
} catch (e) {
print("Error fetching item: $e");
return null;
}
}
}
This is my bloc class:
import 'package:flutter/cupertino.dart';
import 'package:new_flutter/src/models/item_model.dart';
import 'package:new_flutter/src/resources/new_resource_dir/repository_prov2.dart';
class NewsBloc extends ChangeNotifier {
final RepositoryProv2 _prov2 = RepositoryProv2();
List<int> _topIds = [];
final List<ItemModel> _items = [];
bool _isLoading = false;
List<int> get topIds => _topIds;
List<ItemModel> get items => _items;
bool get isLoading => _isLoading;
///Fetch Top IDs from the API
Future<void> fetchTopIds() async {
_isLoading = true;
notifyListeners(); //Notify UI to update
try {
_topIds = await _prov2.fetchTopIds();
} catch (e) {
print('Error fetching top IDs: $e');
} finally {
_isLoading = false;
notifyListeners();
}
}
/// Fetching ITEMS by IDs
Future<ItemModel?> fetchItem(int id) async {
_isLoading = true;
notifyListeners();
try {
final item = await _prov2.fetchItems(id);
if (item != null) {
_items.add(item);
}
} catch (e) {
print('Error fetching item: $e');
} finally {
WidgetsBinding.instance.addPostFrameCallback((_) {
_isLoading = false;
notifyListeners();
});
}
return null;
}
}
this is the app.dart:
import 'package:flutter/material.dart';
import 'package:new_flutter/src/blocs/latest_bloc_provider/news_bloc.dart';
import 'package:new_flutter/src/screens/news_list.dart';
import 'package:provider/provider.dart';
class App extends StatelessWidget {
const App({super.key});
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (context) => NewsBloc(),
child: MaterialApp(
title: 'News!',
home: NewsList(),
),
);
}
}
this is App.dart
import 'package:flutter/material.dart';
import 'package:new_flutter/src/blocs/latest_bloc_provider/news_bloc.dart';
import 'package:new_flutter/src/widgets/news_list_tile.dart';
import 'package:provider/provider.dart';
class NewsList extends StatelessWidget {
const NewsList({super.key});
@override
Widget build(BuildContext context) {
final bloc = Provider.of<NewsBloc>(context);
///Fetch Top IDs when the screen loads
if (bloc.topIds.isEmpty) {
bloc.fetchTopIds();
}
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
backgroundColor: Colors.blue,
elevation: 5,
title: const Text(
'Top News',
style: TextStyle(
color: Colors.white,
),
),
),
body: buildList(bloc),
);
}
Widget buildList(NewsBloc bloc) {
return Consumer<NewsBloc>(
builder: (context, bloc, child) {
if (bloc.isLoading) {
return Center(
child: CircularProgressIndicator(color: Colors.blue),
);
}
return ListView.builder(
itemCount: bloc.topIds.length,
itemBuilder: (context, int index) {
return NewsListTile(
itemId: bloc.topIds[index],
);
},
);
},
);
}
}
This is news list tile class:
import 'package:flutter/material.dart';
import 'package:new_flutter/src/models/item_model.dart';
import 'package:provider/provider.dart';
import '../blocs/latest_bloc_provider/news_bloc.dart';
class NewsListTile extends StatelessWidget {
final int? itemId;
const NewsListTile({super.key, this.itemId});
@override
Widget build(BuildContext context) {
final bloc = Provider.of<NewsBloc>(context);
return FutureBuilder<ItemModel?>(
future: bloc.fetchItem(itemId!),
builder: (context, snapshot) {
// if (!snapshot.hasData) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Text('Still loading item $itemId');
}
if (snapshot.hasError) {
return Text('Error loading item $itemId: ${snapshot.error}');
}
if (!snapshot.hasData) {
return Text('No data available');
}
return Text(snapshot.data!.title);
},
);
}
}
The errors I am receiving from this is confusing and this is it:
======== Exception caught by foundation library ====================================================
The following assertion was thrown while dispatching notifications for NewsBloc:
setState() or markNeedsBuild() called during build.
This _InheritedProviderScope<NewsBloc?> widget cannot be marked as needing to build because the framework is already in the process of building widgets. A widget can be marked as needing to be built during the build phase only if one of its ancestors is currently building. This exception is allowed because the framework builds parent widgets before children, which means a dirty descendant will always be built. Otherwise, the framework might not visit this widget during this build phase.
The widget on which setState() or markNeedsBuild() was called was: _InheritedProviderScope<NewsBloc?>
value: Instance of 'NewsBloc'
listening to value
The widget which was currently being built when the offending call was made was: NewsListTile
dirty
dependencies: [_InheritedProviderScope<NewsBloc?>]
When the exception was thrown, this was the stack:
#0 Element.markNeedsBuild.<anonymous closure> (package:flutter/src/widgets/framework.dart:5177:9)
#1 Element.markNeedsBuild (package:flutter/src/widgets/framework.dart:5189:6)
#2 _InheritedProviderScopeElement.markNeedsNotifyDependents (package:provider/src/inherited_provider.dart:577:5)
#3 ChangeNotifier.notifyListeners (package:flutter/src/foundation/change_notifier.dart:437:24)
#4 NewsBloc.fetchItem (package:new_flutter/src/blocs/latest_bloc_provider/news_bloc.dart:36:5)
#5 NewsListTile.build (package:new_flutter/src/widgets/news_list_tile.dart:17:20)
#6 StatelessElement.build (package:flutter/src/widgets/framework.dart:5687:49)
#7 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5617:15)
The NewsBloc sending notification was: Instance of 'NewsBloc'
====================================================================================================
D/EGL_emulation(13392): app_time_stats: avg=1797.18ms min=1797.18ms max=1797.18ms count=1
I/flutter (13392): Warning database has been locked for 0:00:10.000000. Make sure you always use the transaction object for database operations during a transaction
I/flutter (13392): Warning database has been locked for 0:00:10.000000. Make sure you always use the transaction object for database operations during a transaction
I/flutter (13392): Warning database has been locked for 0:00:10.000000. Make sure you always use the transaction object for database operations during a transaction
I/flutter (13392): Warning database has been locked for 0:00:10.000000. Make sure you always use the transaction object for database operations during a transaction
I/flutter (13392): Warning database has been locked for 0:00:10.000000. Make sure you always use the transaction object for database operations during a transaction
I/flutter (13392): Warning database has been locked for 0:00:10.000000. Make sure you always use the transaction object for database operations during a transaction
I am trying to create a web blog with Hacker News API, but every time I try to run, I get this confusing error thrown at me and I do not get it. asked chatgpt, it gave me a solution but I still run into the same error. I am pasting all necessary classes.
first this is my database class.
import 'dart:io';
import 'package:new_flutter/src/models/item_model.dart';
import 'package:path/path.dart';
import 'package:path_provider/path_provider.dart';
import 'package:sqflite/sqflite.dart';
final newsDbProv2 = NewsDbProv2();
class NewsDbProv2 {
static const _dbName = "items.db";
static const _dbVersion = 1;
static const _tableName = "Items";
Database? db;
/// INITIALIZING THE DATABASE......
Future<void> init() async {
// if (db != null) return;
if (db == null) {
try {
Directory documentsDirectory = await getApplicationDocumentsDirectory();
final path = join(documentsDirectory.path, _dbName);
db = await openDatabase(path,
version: _dbVersion, onCreate: createTable);
} catch (e) {
print('Error initializing database: $e');
}
}
}
///CREATING THE DATABASE TABLE........
Future<void> createTable(Database newDb, int version) async {
try {
await newDb.execute("""
CREATE TABLE $_tableName
(
id INTEGER PRIMARY KEY,
type TEXT,
by TEXT,
time INTEGER,
text TEXT,
parent INTEGER,
kids BLOB,
dead INTEGER,
deleted INTEGER,
url TEXT,
score INTEGER,
title TEXT,
descendants INTEGER
)
""");
} catch (e) {
print('Error creating table: $e');
}
}
///Fetching an ITEM by ID from the DATABASE.....
Future<ItemModel?> fetchItem(int id) async {
try {
if (db == null) await init();
final maps = await db!.query(
_tableName,
columns: null,
where: "id = ?",
whereArgs: [id],
);
if (maps.isNotEmpty) {
return ItemModel.fromDB(maps.first);
}
} catch (e) {
print('Error fetching item from the database: $e');
}
return null;
}
Future<int> addItem(ItemModel item) async {
try {
if (db == null) await init();
return await db!.transaction((txn) async {
return await txn.insert(
// return db!.insert(
_tableName,
item.toMapForDb(),
conflictAlgorithm: ConflictAlgorithm.ignore,
);
});
} catch (e) {
print('Error adding item to database: $e');
return 0;
}
}
// Future<int> addItem(ItemModel item) async {
// return await db!.transaction((txn) async {
// return await txn.insert(
// // return db!.insert(
// _tableName,
// item.toMapForDb(),
// conflictAlgorithm: ConflictAlgorithm.ignore,
// );
// });
// }
}
// Future<List<int>> fetchTopIds() {
// // TODO: implement fetchTopIds
// throw UnimplementedError();
// }
this is the api class
import 'dart:convert';
import 'package:http/http.dart' show Client;
import 'package:new_flutter/src/models/item_model.dart';
const rootDomain = 'https://hacker-news.firebaseio/v0';
class NewsApiProv2 {
Client client = Client();
/// Fetching Top story IDs from the API.......
Future<List<int>> fetchTopIds() async {
try {
final response = await client
.get(Uri.parse('$rootDomain/topstories.json'))
.timeout(const Duration(seconds: 10));
if (response.statusCode == 200) {
final ids = json.decode(response.body);
return ids.cast<int>();
} else {
print("Error: Failed to fetch top IDs. Status: ${response.statusCode}");
}
} catch (e) {
print('Exception during fetchTopIds: $e');
}
return [];
}
/// Fetching a specific Story/ITEM by ID........
Future<ItemModel?> fetchItem(int id) async {
try {
final response = await client
.get(Uri.parse('$rootDomain/item/$id.json'))
.timeout(const Duration(seconds: 10));
if (response.statusCode == 200) {
final parsedJson = json.decode(response.body);
if (parsedJson == null || parsedJson.isEmpty) {
print('Empty or null response for item with id $id');
return null;
}
return ItemModel.fromJson(parsedJson);
} else {
print(
'Failed to fetch item with id $id, Status Code: ${response.statusCode}');
throw Exception('Failed to load item');
}
} catch (e) {
print('Exception during fetchItem: $e');
}
return null;
}
}
This is repository class:
import 'package:new_flutter/src/models/item_model.dart';
import 'package:new_flutter/src/resources/new_resource_dir/news_api_prov2.dart';
import 'package:new_flutter/src/resources/new_resource_dir/news_db_prov_2.dart';
class RepositoryProv2 {
/// Initialize instances of the providers
final NewsApiProv2 _newsApiProvider = NewsApiProv2();
final NewsDbProv2 _newsDbProvider = NewsDbProv2();
/// Fetch the top story IDs from the API
Future<List<int>> fetchTopIds() async {
try {
return await _newsApiProvider.fetchTopIds();
} catch (e) {
print("Error fetching top IDs: $e");
return [];
}
}
/// Fetch an individual item from either API or Database (cache)
Future<ItemModel?> fetchItems(int id) async {
try {
/// First try fetching the item from the database (cache)
ItemModel? item = await _newsDbProvider.fetchItem(id);
/// If the item is not found in the database, fetch from the API
if (item == null) {
item = await _newsApiProvider.fetchItem(id);
/// If the item was found, save it in the database (cache)
if (item != null) {
await _newsDbProvider.addItem(item);
}
}
return item;
} catch (e) {
print("Error fetching item: $e");
return null;
}
}
}
This is my bloc class:
import 'package:flutter/cupertino.dart';
import 'package:new_flutter/src/models/item_model.dart';
import 'package:new_flutter/src/resources/new_resource_dir/repository_prov2.dart';
class NewsBloc extends ChangeNotifier {
final RepositoryProv2 _prov2 = RepositoryProv2();
List<int> _topIds = [];
final List<ItemModel> _items = [];
bool _isLoading = false;
List<int> get topIds => _topIds;
List<ItemModel> get items => _items;
bool get isLoading => _isLoading;
///Fetch Top IDs from the API
Future<void> fetchTopIds() async {
_isLoading = true;
notifyListeners(); //Notify UI to update
try {
_topIds = await _prov2.fetchTopIds();
} catch (e) {
print('Error fetching top IDs: $e');
} finally {
_isLoading = false;
notifyListeners();
}
}
/// Fetching ITEMS by IDs
Future<ItemModel?> fetchItem(int id) async {
_isLoading = true;
notifyListeners();
try {
final item = await _prov2.fetchItems(id);
if (item != null) {
_items.add(item);
}
} catch (e) {
print('Error fetching item: $e');
} finally {
WidgetsBinding.instance.addPostFrameCallback((_) {
_isLoading = false;
notifyListeners();
});
}
return null;
}
}
this is the app.dart:
import 'package:flutter/material.dart';
import 'package:new_flutter/src/blocs/latest_bloc_provider/news_bloc.dart';
import 'package:new_flutter/src/screens/news_list.dart';
import 'package:provider/provider.dart';
class App extends StatelessWidget {
const App({super.key});
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (context) => NewsBloc(),
child: MaterialApp(
title: 'News!',
home: NewsList(),
),
);
}
}
this is App.dart
import 'package:flutter/material.dart';
import 'package:new_flutter/src/blocs/latest_bloc_provider/news_bloc.dart';
import 'package:new_flutter/src/widgets/news_list_tile.dart';
import 'package:provider/provider.dart';
class NewsList extends StatelessWidget {
const NewsList({super.key});
@override
Widget build(BuildContext context) {
final bloc = Provider.of<NewsBloc>(context);
///Fetch Top IDs when the screen loads
if (bloc.topIds.isEmpty) {
bloc.fetchTopIds();
}
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
backgroundColor: Colors.blue,
elevation: 5,
title: const Text(
'Top News',
style: TextStyle(
color: Colors.white,
),
),
),
body: buildList(bloc),
);
}
Widget buildList(NewsBloc bloc) {
return Consumer<NewsBloc>(
builder: (context, bloc, child) {
if (bloc.isLoading) {
return Center(
child: CircularProgressIndicator(color: Colors.blue),
);
}
return ListView.builder(
itemCount: bloc.topIds.length,
itemBuilder: (context, int index) {
return NewsListTile(
itemId: bloc.topIds[index],
);
},
);
},
);
}
}
This is news list tile class:
import 'package:flutter/material.dart';
import 'package:new_flutter/src/models/item_model.dart';
import 'package:provider/provider.dart';
import '../blocs/latest_bloc_provider/news_bloc.dart';
class NewsListTile extends StatelessWidget {
final int? itemId;
const NewsListTile({super.key, this.itemId});
@override
Widget build(BuildContext context) {
final bloc = Provider.of<NewsBloc>(context);
return FutureBuilder<ItemModel?>(
future: bloc.fetchItem(itemId!),
builder: (context, snapshot) {
// if (!snapshot.hasData) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Text('Still loading item $itemId');
}
if (snapshot.hasError) {
return Text('Error loading item $itemId: ${snapshot.error}');
}
if (!snapshot.hasData) {
return Text('No data available');
}
return Text(snapshot.data!.title);
},
);
}
}
The errors I am receiving from this is confusing and this is it:
======== Exception caught by foundation library ====================================================
The following assertion was thrown while dispatching notifications for NewsBloc:
setState() or markNeedsBuild() called during build.
This _InheritedProviderScope<NewsBloc?> widget cannot be marked as needing to build because the framework is already in the process of building widgets. A widget can be marked as needing to be built during the build phase only if one of its ancestors is currently building. This exception is allowed because the framework builds parent widgets before children, which means a dirty descendant will always be built. Otherwise, the framework might not visit this widget during this build phase.
The widget on which setState() or markNeedsBuild() was called was: _InheritedProviderScope<NewsBloc?>
value: Instance of 'NewsBloc'
listening to value
The widget which was currently being built when the offending call was made was: NewsListTile
dirty
dependencies: [_InheritedProviderScope<NewsBloc?>]
When the exception was thrown, this was the stack:
#0 Element.markNeedsBuild.<anonymous closure> (package:flutter/src/widgets/framework.dart:5177:9)
#1 Element.markNeedsBuild (package:flutter/src/widgets/framework.dart:5189:6)
#2 _InheritedProviderScopeElement.markNeedsNotifyDependents (package:provider/src/inherited_provider.dart:577:5)
#3 ChangeNotifier.notifyListeners (package:flutter/src/foundation/change_notifier.dart:437:24)
#4 NewsBloc.fetchItem (package:new_flutter/src/blocs/latest_bloc_provider/news_bloc.dart:36:5)
#5 NewsListTile.build (package:new_flutter/src/widgets/news_list_tile.dart:17:20)
#6 StatelessElement.build (package:flutter/src/widgets/framework.dart:5687:49)
#7 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5617:15)
The NewsBloc sending notification was: Instance of 'NewsBloc'
====================================================================================================
D/EGL_emulation(13392): app_time_stats: avg=1797.18ms min=1797.18ms max=1797.18ms count=1
I/flutter (13392): Warning database has been locked for 0:00:10.000000. Make sure you always use the transaction object for database operations during a transaction
I/flutter (13392): Warning database has been locked for 0:00:10.000000. Make sure you always use the transaction object for database operations during a transaction
I/flutter (13392): Warning database has been locked for 0:00:10.000000. Make sure you always use the transaction object for database operations during a transaction
I/flutter (13392): Warning database has been locked for 0:00:10.000000. Make sure you always use the transaction object for database operations during a transaction
I/flutter (13392): Warning database has been locked for 0:00:10.000000. Make sure you always use the transaction object for database operations during a transaction
I/flutter (13392): Warning database has been locked for 0:00:10.000000. Make sure you always use the transaction object for database operations during a transaction
Share
Improve this question
asked Nov 17, 2024 at 3:31
Fred_WolfeFred_Wolfe
8722 gold badges11 silver badges22 bronze badges
3
|
1 Answer
Reset to default 1According to
#4 NewsBloc.fetchItem (package:new_flutter/src/blocs/latest_bloc_provider/news_bloc.dart:36:5)
the error occurred in bloc class NewsBloc
line 36, wrap notifyListeners()
in WidgetsBinding.instance.addPostFrameCallback
might solve this problem.
/// Fetching ITEMS by IDs
Future<ItemModel?> fetchItem(int id) async {
_isLoading = true;
WidgetsBinding.instance.addPostFrameCallback((_) {
notifyListeners();
});
try {
final item = await _prov2.fetchItems(id);
if (item != null) {
_items.add(item);
}
} catch (e) {
print('Error fetching item: $e');
} finally {
WidgetsBinding.instance.addPostFrameCallback((_) {
_isLoading = false;
notifyListeners();
});
}
return null;
}
notifyListeners
insidefetchItems
method: it forces another rebuild while building yourNewsListTile
– pskink Commented Nov 17, 2024 at 7:46