I'm creating an app for my new blog, which is hosted on blogger.com. I've got the posts to get displayed in the app along with the images, but for some reason I can't get the text in the post to be formatted. It has just bunched it all together.
Here's how it looks in the blog & in the app:
How can I format this in my code so that it looks like it does in the blog?
Here's the code I'm using in the main.dart file:
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
#override
_fetchData() async {
print("attempting");
final url =
"https://www.googleapis.com/blogger/v3/blogs/8902712678213444829/posts/?key=AIzaSyDpwI-kMZ_IxqAJVBKAVtWLOlaGQ5YLEuw";
final response = await http.get(url);
print(response.body);
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://www.googleapis.com/blogger/v3/blogs/8902712678213444829/posts/?key=AIzaSyDpwI-kMZ_IxqAJVBKAVtWLOlaGQ5YLEuw";
}
}
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.fitHeight,
//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://www.googleapis.com/blogger/v3/blogs/8902712678213444829/posts/?key=AIzaSyDpwI-kMZ_IxqAJVBKAVtWLOlaGQ5YLEuw")),
),
),
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(),
],
),
);
},
)));
}
}
And here's the code I'm using in the post_view.dart file:
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://www.googleapis.com/blogger/v3/blogs/8902712678213444829/posts/?key=AIzaSyDpwI-kMZ_IxqAJVBKAVtWLOlaGQ5YLEuw")),
),
),
),
new Padding(
padding:
const EdgeInsets.symmetric(vertical: 16.0, horizontal: 16.0),
child: new Text(
desc,
style: new TextStyle(
fontSize: 18.0,
),
),
),
],
))),
);
}
}
EDITED:
Here's what get's printed in the console when I hot reload the app:
I/flutter ( 9778): attempting
I/flutter ( 9778): {
I/flutter ( 9778): "kind": "blogger#postList",
I/flutter ( 9778): "items": [
I/flutter ( 9778): {
I/flutter ( 9778): "kind": "blogger#post",
I/flutter ( 9778): "id": "3086822326789809431",
I/flutter ( 9778): "blog": {
I/flutter ( 9778): "id": "8902712678213444829"
I/flutter ( 9778): },
I/flutter ( 9778): "published": "2020-06-15T00:22:00-07:00",
I/flutter ( 9778): "updated": "2020-06-15T22:19:56-07:00",
I/flutter ( 9778): "url": "http://lessmeatapp.blogspot.com/2020/06/mushroom-tagine.html",
I/flutter ( 9778): "selfLink": "https://www.googleapis.com/blogger/v3/blogs/8902712678213444829/posts/3086822326789809431",
I/flutter ( 9778): "title": "Dummy Post 3",
I/flutter ( 9778): "content": "\u003cbr /\u003e\u003cdiv class=\"separator\" style=\"clear: both; text-align: center;\"\u003e\u003ca href=\"https://1.bp.blogspot.com/-Vv9KcxNHxhU/XuhVnskHvCI/AAAAAAAAAGw/z7tH271PrIEvkQam74G497Gw4A-eFondACK4BGAsYHg/s1400/DD-Grunge-United-Kingdom-Flag-88837-Preview.jpg\" imageanchor=\"1\" style=\"margin-left: 1em; margin-right: 1em;\"\u003e\u003cimg border=\"0\" data-original-height=\"980\" data-original-width=\"1400\" src=\"https://1.bp.blogspot.com/-Vv9KcxNHxhU/XuhVnskHvCI/AAAAAAAAAGw/z7tH271PrIEvkQam74G497G
I/flutter ( 9778): https://1.bp.blogspot.com/-Vv9KcxNHxhU/XuhVnskHvCI/AAAAAAAAAGw/z7tH271PrIEvkQam74G497Gw4A-eFondACK4BGAsYHg/s1400/DD-Grunge-United-Kingdom-Flag-88837-Preview
I/flutter ( 9778): https://1.bp.blogspot.com/-hvzDDsO44FI/XuhWvqDjRwI/AAAAAAAAAHI/mBjWane0s5wtdJnkLDrNrmyprVoNeWDagCK4BGAsYHg/s1400/DD-Patriotic-Retro-Background-33092-Preview
I/flutter ( 9778): https://1.bp.blogspot.com/-efv2-Ikiyr8/XuhX-YLtDDI/AAAAAAAAAH0/JJE2mrOU-HMsq6Adu1whv5b3W10yqkRlQCK4BGAsYHg/s1400/20
in response, content section is HTML
and for render HTML in the flutter, you should use fultter_html package
you can find it in this link
and with this, you don't need export images from your content
if you have any question ask me in comments
in your PostView build method pass description variable to HTML widget:
Html(
data: description,
//Optional parameters:
backgroundColor: Colors.white70,
Related
I have an album screen that uses MultiBlocProvider to create three cubits, one for each tab of the album page. Each tab page reads its own cubit using context.read() and then uses the cubit in order to fetch some data asynchronously that will eventually also change the cubit's state.
I've noticed that although the app functions just fine, when switching between tabs, an error comes up in the logs stating “Looking up a deactivated widget's ancestor is unsafe…”. From what I've read, this is probably caused due to the context changing, which is a result of the widget being moved around in the widget tree. Is my understanding correct?
In order to give a more specific example, here's my album screen:
class AlbumScreen extends StatelessWidget {
const AlbumScreen({Key? key}) : super(key: key);
static const String path = '/albumScreen';
#override
Widget build(BuildContext context) {
return MultiBlocProvider(
providers: [
BlocProvider<AlbumArtworksCubit>(
create: (BuildContext context) => AlbumArtworksCubit(),
),
BlocProvider<AlbumArtistsCubit>(
create: (BuildContext context) => AlbumArtistsCubit(),
),
BlocProvider<AlbumGalleriesCubit>(
create: (BuildContext context) => AlbumGalleriesCubit(),
),
],
child: const AlbumComponent(),
);
}
}
Here's its component:
class AlbumComponent extends StatefulWidget {
const AlbumComponent({Key? key}) : super(key: key);
#override
State<AlbumComponent> createState() => _AlbumComponentState();
}
class _AlbumComponentState extends State<AlbumComponent>
with SingleTickerProviderStateMixin {
TabController? _controller;
#override
void initState() {
super.initState();
_controller = TabController(length: 3, vsync: this, initialIndex: 0);
_controller?.addListener(() {
setState(() {});
});
}
#override
void dispose() {
_controller?.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
appBar: PreferredSize(
preferredSize: const Size.fromHeight(100),
child: ArtVoltAppBar(
title: '',
hasLogo: true,
hasBorder: false,
actions: <Widget>[
Align(
alignment: Alignment.topRight,
child: GestureDetector(
onTap: () => NavigatorUtils.goToSettingsScreen(context),
child: Padding(
padding: const EdgeInsets.only(top: 12, right: 12),
child: Image.asset(
'assets/icons/settings_icon.png',
height: 24,
width: 24,
),
),
),
),
],
bottom: TabBar(
controller: _controller,
tabs: [
Tab(
child: Text(
'Artworks',
style: montserratSemiBold16.copyWith(
color: _controller?.index == 0
? AppColors.black
: AppColors.grey,
),
),
),
Tab(
child: Text(
'Artists',
style: montserratSemiBold16.copyWith(
color: _controller?.index == 1
? AppColors.black
: AppColors.grey,
),
),
),
Tab(
child: Text(
'Galleries',
style: montserratSemiBold16.copyWith(
color: _controller?.index == 2
? AppColors.black
: AppColors.grey,
),
),
),
],
labelColor: AppColors.black,
labelStyle: montserratSemiBold16.copyWith(color: AppColors.black),
indicatorColor: AppColors.black,
unselectedLabelColor: AppColors.red,
indicatorSize: TabBarIndicatorSize.label,
isScrollable: true,
indicatorWeight: 5,
),
),
),
body: Padding(
padding: const EdgeInsets.only(top: 16, bottom: 16),
child: TabBarView(
controller: _controller,
children: [
Artworks(
key: UniqueKey(),
),
Artists(
key: UniqueKey(),
),
Galleries(
key: UniqueKey(),
),
],
),
),
),
);
}
}
And here's one of the tabs (Galleries()) for the sake of keeping the post relatively small:
class Galleries extends StatefulWidget {
const Galleries({Key? key}) : super(key: key);
#override
State<Galleries> createState() => _GalleriesState();
}
class _GalleriesState extends State<Galleries> {
AlbumGalleriesCubit get _cubit => context.read<AlbumGalleriesCubit>();
List<AzItem<Gallery>> items = [];
List<String> _alphabet = [];
final IndexBarDragListener _indexListener = IndexBarDragListener.create();
String _topLetter = 'A';
String _lastIndexItemPressed = 'A';
#override
void initState() {
super.initState();
initData();
}
Future<void> initData() async {
await _cubit.fetchAllGalleries();
items = _cubit.galleries
.map(
(Gallery gallery) => AzItem<Gallery>(
data: gallery,
text: gallery.name,
tag: gallery.name[0].toUpperCase(),
),
)
.toList();
SuspensionUtil.sortListBySuspensionTag(items);
SuspensionUtil.setShowSuspensionStatus(items);
_alphabet = items
.map((AzItem<Gallery> item) => item.getSuspensionTag())
.toSet()
.toList();
_indexListener.dragDetails.addListener(() {
setState(() {
_topLetter = _indexListener.dragDetails.value.tag ?? 'A';
_lastIndexItemPressed = _indexListener.dragDetails.value.tag ?? 'A';
});
});
setState(() {});
}
#override
void dispose() {
_cubit.close();
super.dispose();
}
#override
Widget build(BuildContext context) {
if (_cubit.galleries.isEmpty &&
(_cubit.state is! AlbumGalleriesLoaded$ ||
_cubit.state is AlbumGalleriesLoading$)) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: const <Widget>[
CircularProgressIndicator(color: AppColors.red),
SizedBox(
height: 10,
),
Text(
'Loading galleries',
style: montserratMedium14,
)
],
),
);
} else if (_cubit.galleries.isEmpty &&
_cubit.state is AlbumGalleriesLoaded$) {
return const Center(
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 24),
child: Text(
initialEmptyAlbumMsg,
style: montserratMedium14,
textAlign: TextAlign.center,
),
),
);
} else {
return Stack(
children: <Widget>[
AzListView(
data: items,
itemCount: items.length,
indexHintBuilder: (_, __) => const SizedBox.shrink(),
indexBarData: _alphabet,
indexBarDragListener: _indexListener,
indexBarOptions: const IndexBarOptions(
textStyle: TextStyle(color: AppColors.blue),
),
itemBuilder: (BuildContext context, int index) {
final AzItem<Gallery> item = items[index];
final String tag = item.getSuspensionTag();
final bool offstage = !item.isShowSuspension;
return Column(
children: <Widget>[
VisibilityDetector(
key: Key('$tag-$index'),
onVisibilityChanged: (VisibilityInfo info) {
if (info.key.toString() == '[<\'$tag-$index\'>]') {
if (info.visibleFraction == 0.0) {
if (_topLetter.compareTo(tag) < 0) {
if ((_alphabet.indexOf(tag) -
_alphabet.indexOf(_topLetter)) ==
1) {
setState(() => _topLetter = tag);
}
}
} else if (info.visibleFraction == 1.0) {
if (_topLetter.compareTo(tag) == 0 && tag != 'A') {
if (_lastIndexItemPressed != tag) {
setState(() {
_topLetter =
items[index - 1].getSuspensionTag();
_lastIndexItemPressed = 'A';
});
}
}
}
}
},
child: Offstage(
offstage: offstage,
child: Container(
height: 30,
color: Colors.white,
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 16),
alignment: Alignment.centerLeft,
child: Text(
tag,
style: montserratSemiBold16.copyWith(
color: AppColors.grey),
),
),
),
),
),
GestureDetector(
onTap: () => NavigatorUtils.goToGalleryViewScreen(
context,
gallery: item.data!,
),
child: Container(
color: AppColors.white,
child: Column(
children: <Widget>[
const AppDivider(),
Row(
children: <Widget>[
Container(
height: 56,
width: 56,
margin: const EdgeInsets.only(
left: 24, right: 16, top: 4, bottom: 4),
decoration: BoxDecoration(
color: AppColors.white,
border: Border.all(
width: 1, color: AppColors.grey),
borderRadius: BorderRadius.circular(8),
),
child: CachedNetworkImage(
placeholder: (context, url) => const Center(
child: CircularProgressIndicator(
color: AppColors.red,
),
),
errorWidget: (context, url, dynamic error) =>
const Icon(Icons.error),
imageUrl: item.data?.thumbnailPhoto ??
item.data?.firebasePhoto ??
'',
fit: BoxFit.contain,
),
),
Text(item.text, style: montserratMedium14),
],
)
],
),
),
),
],
);
},
),
Container(
height: 30,
color: AppColors.white,
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 16),
alignment: Alignment.centerLeft,
decoration: BoxDecoration(
gradient: LinearGradient(
colors: <Color>[
AppColors.grey.withOpacity(.2),
AppColors.grey.withOpacity(.1),
],
),
),
child: Text(
_topLetter,
style: montserratSemiBold16.copyWith(color: AppColors.grey),
),
),
),
],
);
}
}
}
With the above in mind, if I navigate to the Galleries tab and then switch to another tab (i.e. Artists()), the error is the following:
[ ] E/flutter ( 4652): [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: Looking up a deactivated widget's ancestor is unsafe.
[ ] E/flutter ( 4652): At this point the state of the widget's element tree is no longer stable.
[ ] E/flutter ( 4652): To safely refer to a widget's ancestor in its dispose() method, save a reference to the ancestor by calling dependOnInheritedWidgetOfExactType() in the widget's didChangeDependencies() method.
[ ] E/flutter ( 4652): #0 Element._debugCheckStateIsActiveForAncestorLookup.<anonymous closure>
package:flutter/…/widgets/framework.dart:4241
[ ] E/flutter ( 4652): #1 Element._debugCheckStateIsActiveForAncestorLookup
package:flutter/…/widgets/framework.dart:4255
[ ] E/flutter ( 4652): #2 Element.getElementForInheritedWidgetOfExactType
package:flutter/…/widgets/framework.dart:4286
[ ] E/flutter ( 4652): #3 Provider._inheritedElementOf
package:provider/src/provider.dart:339
[ ] E/flutter ( 4652): #4 Provider.of
package:provider/src/provider.dart:293
[ ] E/flutter ( 4652): #5 ReadContext.read
package:provider/src/provider.dart:649
[ ] E/flutter ( 4652): #6 _GalleriesState._cubit
package:artvolt_flutter/…/widgets/galleries.dart:28
[ ] E/flutter ( 4652): #7 _GalleriesState.initData
package:artvolt_flutter/…/widgets/galleries.dart:47
[ ] E/flutter ( 4652): <asynchronous suspension>
The error points to line 47 of the Galleries tab, which is:
items = _cubit.galleries
.map(
(Gallery gallery) => AzItem<Gallery>(
data: gallery,
text: gallery.name,
tag: gallery.name[0].toUpperCase(),
),
)
.toList();
As I understand, this part of the initData() call is still running despite me having moved to a different tab. The tab switch has caused the galleries widget to change position in the widget tree thus changing the context which in turn results to the error that I am seeing. Is this approximately correct?
If so, what is the best way to handle such lifecycle cases? Is there a way for me to stop all pending work on the Galleries widget when the cubit's context has changed in order to avoid the error? I read a bit about the didChangeDependencies() method but I'm not sure if I should use it to "fix" the error since the source may be my bad state management code.
I'm trying to use Provider with MVVM architecture, I have a very strange error with provider, on the main page there are 3 sections, banner, discounts and categories, when I change the quantity of goods in discounts, everything works, but after switching to a category and back, it already gives an error when I change the quantity of goods,
the data is not null, I think the problem is with the provider, the scheme is as follows:
discount section-> quantity of goods -> works
home-> category-> go back-> quantity of goods in discounts-> not working
Demo project
The following _CastError was thrown building MainPage(dirty, dependencies: [_InheritedProviderScope<AllGoodsViewModel?>, _LocalizationsScope-[GlobalKey#a9792], MediaQuery, _InheritedProviderScope<MainPageListViewModel?>], state: _MainPageState#eb568):
Null check operator used on a null value
The relevant error-causing widget was:
MainPage MainPage:file:///Users/.../lib/main.dart:93:21
When the exception was thrown, this was the stack:
#0 Element.widget (package:flutter/src/widgets/framework.dart:3229:31)
#1 debugCheckHasMediaQuery.<anonymous closure> (package:flutter/src/widgets/debug.dart:245:17)
#2 debugCheckHasMediaQuery (package:flutter/src/widgets/debug.dart:261:4)
#3 MediaQuery.of (package:flutter/src/widgets/media_query.dart:908:12)
#4 ScreenUtil.screenWidth (package:flutter_screenutil/src/screen_util.dart:148:37)
#5 ScreenUtil.scaleWidth (package:flutter_screenutil/src/screen_util.dart:167:28)
#6 ScreenUtil.setWidth (package:flutter_screenutil/src/screen_util.dart:182:41)
#7 SizeExtension.w (package:flutter_screenutil/src/size_extension.dart:9:32)
#8 _MainPageState.build (package:.../View/MainPage.dart:240:78)
#9 StatefulElement.build (package:flutter/src/widgets/framework.dart:4919:27)
#10 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4806:15)
#11 StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:4977:11)
#12 Element.rebuild (package:flutter/src/widgets/framework.dart:4529:5)
#13 BuildOwner.buildScope (package:flutter/src/widgets/framework.dart:2659:19)
#14 WidgetsBinding.drawFrame (package:flutter/src/widgets/binding.dart:891:21)
#15 RendererBinding._handlePersistentFrameCallback (package:flutter/src/rendering/binding.dart:370:5)
#16 SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:1146:15)
#17 SchedulerBinding.handleDrawFrame (package:flutter/src/scheduler/binding.dart:1083:9)
#18 SchedulerBinding._handleDrawFrame (package:flutter/src/scheduler/binding.dart:997:5)
#22 _invoke (dart:ui/hooks.dart:151:10)
#23 PlatformDispatcher._drawFrame (dart:ui/platform_dispatcher.dart:308:5)
#24 _drawFrame (dart:ui/hooks.dart:115:31)
(elided 3 frames from dart:async)
ScreenUtilInitService
class ScreenUtilInitService z{
/// A helper widget that initializes [ScreenUtil]
ScreenUtilInitService({required this.builder, Key? key,}) : super(key: key);
final Widget Function(BuildContext) builder;
#override
Widget build(BuildContext context) {
return ScreenUtilInit(
designSize: Size(375, 812),
builder: (context, widget) => builder(context)
);
}
}
class MyApp extends StatefulWidget {
#override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
bool _initialized = false;
bool _error = false;
Widget _present = SplashScreen();
void initializeFlutterFire() async {
try {
if (Platform.isAndroid) {
await AndroidInAppWebViewController.setWebContentsDebuggingEnabled(true);
}
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
await message();
await rootWidget();
setState(() {
_initialized = true;
});
} catch(e) {
setState(() {
_error = true;
});
}
}
Future<void> rootWidget() async {
final prefs = await SharedPreferences.getInstance();
final id = prefs.get("idAddress");
final logged = prefs.getBool("logged") ?? false;
if ((FirebaseAuth.instance.currentUser?.uid != null && logged) || id != null) {
setState(() {
_present = MainPage();
});
return;
} else {
setState(() {
_present = OfferPage();
});
return;
}
}
#override
void initState() {
initializeDB();
initializeFlutterFire();
super.initState();
listeners();
}
#override
Widget build(BuildContext context) {
if(_error) {
return MaterialApp(home:SplashScreen());
}
if (!_initialized) {
return MaterialApp(home:SplashScreen());
}
return RootPage().mainPage(present: _present);
}
}
and RootPage
class RootPage {
Widget mainPage({Widget? present}){
return MaterialApp(
initialRoute: '/',
routes: {
'/ProfilePage': (context) => ProfilePage(),
'/MainPage': (context) => MainPage(),
'/CartPage': (context) => CartPage(),
},
builder: (context, widget) {
return ScreenUtilInitService(
builder: (context) => widget!
);
},
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MultiProvider(
providers: [
ChangeNotifierProvider(
create: (context) => MainPageListViewModel(),
),
ChangeNotifierProvider(
create: (context) => CartViewModel(),
child: CartPage()
),
ChangeNotifierProvider(
create: (context) => AllGoodsViewModel(),
),
ChangeNotifierProvider(
create: (context) => GoodsViewModel(),
),
],
child: present != null ? present : MainPage(),
),
);
}
}
MainPage
#override
void initState() {
setting();
super.initState();
_scrollListener();
_notification();
}
#override
void dispose() {
_scrollController.removeListener(() { });
_scrollController.dispose();
NotificationCenter().unsubscribe('cart');
NotificationCenter().unsubscribe('address');
super.dispose();
}
void setting() async {
final cart = await SQFliteService.cart.getCount();
final address = await SQFliteService.location.current();
setState((){
_address = address;
showCart = cart == 0 ? false : true;
});
Provider.of<MainPageListViewModel>(context, listen: false).deliveryRequest() ;
Provider.of<MainPageListViewModel>(context, listen: false).fetchBanner();
Provider.of<MainPageListViewModel>(context, listen: false).fetchCategory();
Provider.of<AllGoodsViewModel>(context, listen: false).fetchSale();
Provider.of<MainPageListViewModel>(context, listen: false).fetchLaunchMessage(context);
}
void _scrollListener(){
_scrollController.addListener(() {
///setState is null after pop after category
// setState(() {
// _bottomOffSet = _scrollController.offset;
// });
});
}
void _notification(){
NotificationCenter().subscribe('cart', () async {
final result = await SQFliteService.cart.getCount();
setState(() {
showCart = result == 0 ? false : true;
print("update");
});
});
NotificationCenter().subscribe('address', () async {
final result = await SQFliteService.location.current();
setState((){
_address = result;
});
});
}
#override
Widget build(BuildContext context) {
final models = Provider.of<MainPageListViewModel>(context);
final sale = Provider.of<AllGoodsViewModel>(context);
final size = MediaQuery.of(context).size;
return Container(
child: CustomScrollView(
physics: AlwaysScrollableScrollPhysics(),
controller: _scrollController,
slivers: [
SliverAppBar(
...
),
SliverList(
delegate: SliverChildBuilderDelegate((context, index) => Container(
color: Colors.transparent,
child: Column(
children: [
bannerW(),
ViewSale(model: sale.goods),
SizedBox(height: 10),
ViewCategory(model: models.category)
]
),
),
childCount: 1
),
),
],
),
);
ViewSale
class _ViewSaleState extends State<ViewSale> {
#override
Widget build(BuildContext context) {
return Column(
children: [
Container(
margin: EdgeInsets.only(left: 16, right: 16, bottom: 15),
child: Align(
alignment: Alignment.centerLeft,
child: Text("Скидки", style: TextStyle(fontFamily: "Inter", fontWeight: FontWeight.w900, fontSize: 18))),
),
Container(
height: ((MediaQuery.of(context).size.width/2.4) - 21) + 71,
width: MediaQuery.of(context).size.width,
child: ListView.builder(
padding: EdgeInsets.symmetric(horizontal: 16),
shrinkWrap: true,
itemCount: widget.model.length,
scrollDirection: Axis.horizontal,
itemBuilder: (item, index) {
return ChangeNotifierProvider(
create: (context) => AllGoodsViewModel(),
child: ViewGoodsSale(model: widget.model[index])
);
}),
)
]
);
}
}
ViewSaleGoods
class _ViewGoodsSaleState extends State<ViewGoodsSale> {
GlobalKey _key = GlobalKey();
#override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_){
Provider.of<AllGoodsViewModel>(context, listen: false).setting(widget.model);
});
}
#override
Widget build(BuildContext context) {
final model = Provider.of<AllGoodsViewModel>(context);
final size = MediaQuery.of(context).size;
Widget inCart(){
return Container(
key: _key,
height: 31,
child: GestureDetector(
onPanDown: (details) {
Goods? item = widget.model;
RenderBox _cardBox = _key.currentContext!.findRenderObject() as RenderBox;
final localPosition = details.localPosition;
final localDx = localPosition.dx;
if (localDx <= _cardBox.size.width/2) {
Goods value = cart.firstWhere((element) => element.id == item.id);
if (item.optState == 0 ? value.orderCount <= 1 : value.orderCount <= value.opt!.count) {
setState(() {
context.read<AllGoodsViewModel>().setCountInCart(0);
final ind = cart.indexWhere((element) => element.id == item.id);
if (ind != -1) {
cart[ind].orderCount = 0;
SQFliteService.cart.delete(cart[ind].id);
cart.removeAt(ind);
}
});
} else {
model.haveItem(item: item, operation: item.optState == 0 ? -1 : (-1 * value.opt!.count));
}
} else {
model.haveItem(item: item, operation: item.optState == 0 ? 1 : item.count);
}
},
child: TextButton(
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all(Design.appColor),
padding: MaterialStateProperty.all(EdgeInsets.symmetric(vertical: 8, horizontal: 10)),
shape: MaterialStateProperty.all(RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0),
))
),
onPressed: (){},
child: Container(
child: RichText(
text: TextSpan(
text: "",
children:[
WidgetSpan(
alignment: PlaceholderAlignment.middle,
child: Icon(Icons.remove, size: 14, color: Colors.white),
),
TextSpan(
text: " ${widget.model.optState == 0 ? (widget.model.minPrice ?? widget.model.price) : widget.model.opt!.price} ₽ ",
style: TextStyle(
color: Colors.white,
fontSize: 14,
fontWeight: FontWeight.w500,
fontFamily: "Inter"
),
),
WidgetSpan(
alignment: PlaceholderAlignment.middle,
child: Icon(Icons.add, size: 14, color: Colors.white),
)
],
),
),
),
),
),// Your TextButton code goes here.
);
}
Widget noInCart(){
return Container(
key: _key,
height: 31,
child: TextButton(
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all(model.orderBg),
padding: MaterialStateProperty.all(EdgeInsets.symmetric(vertical: 8, horizontal: 10)),
shape: MaterialStateProperty.all(RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0),
))
),
onPressed: (){
Goods? item = widget.model;
model.haveItem(item: item, operation: item.optState == 0 ? 1 : item.count);
},
child: Container(
child: RichText(
text: TextSpan(
text: "${widget.model.optState == 0 ? widget.model.minPrice == null ? widget.model.price : widget.model.minPrice : widget.model.opt!.price} ₽ ",
style: TextStyle(
color: widget.model.minPrice != null ? Design.grey : Colors.black,
decoration: widget.model.optState == 0 && widget.model.minPrice != null ? TextDecoration.lineThrough : TextDecoration.none,
fontSize: 14,
fontWeight: FontWeight.w500,
fontFamily: "Inter"
),
children:[
TextSpan(
text: widget.model.minPrice == null ? "" : " ${widget.model.price} ₽",
style: TextStyle(
color: Colors.black,
decoration: TextDecoration.none,
fontSize: 14,
fontWeight: FontWeight.w500,
fontFamily: "Inter"
),
),
WidgetSpan(
alignment: PlaceholderAlignment.middle,
child: Icon(Icons.add, size: 14, color: Colors.black),
style: TextStyle(
color: Colors.black,
decoration: TextDecoration.none,
),
)
],
),
),
),
),
);
}
Widget card({required Size size}) {
return Container(
color: Colors.white,
width: (size.width/2.4) - 11,
margin: EdgeInsets.only(right: 10),
child: Column(
children: [
Stack(
children: [
Container(
height: (size.width/2.4) - 21,
padding: EdgeInsets.all(1),
decoration: BoxDecoration(
image: DecorationImage(
fit: BoxFit.contain,
image: NetworkImage(widget.model.images.first)
),
borderRadius: BorderRadius.all(Radius.circular(10.0)),
border: Border.all(
width: 1,
color: Design.lightGrey
),
),
),
Visibility(
visible: context.read<AllGoodsViewModel>().countInCart == 0 ? false : true,
child: Container(
height: (size.width/2.4) - 21,
decoration: BoxDecoration(
color: Colors.black.withOpacity(0.5),
borderRadius: BorderRadius.all(Radius.circular(10.0)),
),
child: Visibility(
visible: true,
child: Center(
child: model.orderCountText,
),
),
),
)
]
),
SizedBox(height: 5),
Align(
alignment: Alignment.centerLeft,
child: Container(
height: 29,
child: Text(widget.model.name,
maxLines: 2,
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w700,
fontFamily: "Inter",
),
),
),
),
SizedBox(height: 6),
Align(
alignment: Alignment.centerLeft,
child: (context.read<AllGoodsViewModel>().countInCart == 0) ? noInCart() : inCart()
)
],
),
);
}
return card(size: size);
}
}
ViewCategory
class _ViewCategoryState extends State<ViewCategory> {
#override
Widget build(BuildContext context) {
return Container(
child: GridView.builder(
padding: EdgeInsets.all(16),
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
childAspectRatio: 1),
itemCount: widget.model.length,
itemBuilder: (context, index) {
return GestureDetector(
child: Stack(
children: [
Container(
decoration: BoxDecoration(
color: Design.lightGrey,
borderRadius: BorderRadius.all(Radius.circular(10.0)),
),
margin: EdgeInsets.only(right: (index.isOdd ? 0 : 5) , left: (index.isOdd ? 5 : 0), bottom: 10 ),
child: Image.network(widget.model[index].category?.url ?? "")
),
Positioned(
left: 10,
right: 10,
top: 8,
child: Text(widget.model[index].category?.name ?? "", style: TextStyle(fontFamily: "Inter", fontWeight: FontWeight.w500, fontSize: 12.sp)),
)
]
),
onTap: () async {
if (widget.model[index].category != null) {
final data = widget.model[index].category!;
final category = CategoryData(id: data.id, name: data.name);
if (data.tags == null) {
Navigator.of(context).push(
MaterialPageRoute(builder: (context) =>
ChangeNotifierProvider.value(value: AllGoodsViewModel(),
child: AllCategoryGoodsPage(category: category))
)
);
} else {
Navigator.of(context).push(
MaterialPageRoute(builder: (context) =>
ChangeNotifierProvider.value(value: GoodsViewModel(),
child: GoodsPage(category: category))
)
);
}
FirebaseAnalytics.instance.logEvent(name: "Category", parameters: null);
}
}
);
},
),
);
}
}
"Give a man a fish, feed him for a day. Teach a man to fish, feed him for a lifetime."
Thus, I try to explain what to do generally.
Firstly, look at the error stacktrace. You only provide the error message without stacktrace - thus no wonder nobody can see which line of code throws the error.
P.S. If you cannot find the stack trace, search or create a separate question (in some strange cases stack trace is not easy to find).
When you get the stack trace, you can read which line of code has this null pointer error, and which lines are calling that function. Suppose it is a.dart line 123 throwing the error, then look at that line and examine why that happens.
You may want to do "logging" or "debugging" to dig out further clues.
Then the bug should be found and solved easily. If not, try to create a minimal reproducible sample - your current sample is still very big and it is hard for people to dig and find bug for you :)
Your IDE will usually have great stack trace debugging. If you are using vscode, you can turn on the following two options for your error tracking.
If you're using Android Studio. Run -> View Breakpoints.
"Null check operator used on a null value" is a error you get when you use a bang operator (!) on a nullable instance which wasn't initialized.
In your "ViewSaleGoods" file you have it at -
if (item.optState == 0 ? value.orderCount <= 1 : value.orderCount <= value.opt!.count) { // Here .....
setState(() {
context.read<AllGoodsViewModel>().setCountInCart(0);
final ind = cart.indexWhere((element) => element.id == item.id);
if (ind != -1) {
cart[ind].orderCount = 0;
SQFliteService.cart.delete(cart[ind].id);
cart.removeAt(ind);
}
});
} else {
model.haveItem(item: item, operation: item.optState == 0 ? -1 : (-1 * value.opt!.count)); // here...
}
TextSpan(
text: " ${widget.model.optState == 0 ? (widget.model.minPrice ?? widget.model.price) : widget.model.opt!.price} ₽ ", // and here ....
style: TextStyle(
color: Colors.white,
fontSize: 14,
fontWeight: FontWeight.w500,
fontFamily: "Inter"
),
),
so anyone of those is turning out null so the error. Now to fix it you can use some ways like -
// Use variable and if check
var nullSafe;
if (value.opt != null) {
nullSafe = value.opt; // now you can use these variable instead
}
// Or use ?. and ??
value.opt?count ?? "some default value"
Hope it helps. 😄
I solved the problem, the error was not at all in the provider, but in ScreenUtilInitService I changed it to
class ScreenUtilInitService {
Widget build(Function(BuildContext) builder) {
return ScreenUtilInit(
designSize: Size(375, 812),
builder: (context, widget) => builder(context)
);
}
}
and removed ScreenUtilInitService from GoodsPage and AllCategoryGoodsPage so ScreenUtilInitService is not redefined as a widget, which resulted in the error
Hello I am new in Flutter and I am currently building a chat application.
I got a profile maker screen where the user can upload an image to set their avatar. I am using CircularProgressIndicator() to show an uploading screen. I want to know that how can I navigate to the next screen i.e my main homescreen automatically after uploading completed so that the user doesn't have to wait for any button to press.
Here is the code which I tried
progressString != '100% Completed' ? Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
CircularProgressIndicator(
backgroundColor: Colors.blue,
),
SizedBox(
height: 20.0,
),
Text("Uploading File : $progressString",
style: TextStyle(
color: Colors.white54,
fontSize: 20.0,
fontWeight: FontWeight.w900,
),
),
],
) : Navigator.pushReplacement(context,
MaterialPageRoute(builder: (context) {
return LoginPage();
}),
),
Upload Code
FormData data = FormData.fromMap({
"username": userName.toString(),
"name": naMe.toString(),
"birthday": birthDay.toString(),
"about": aboutUser.toString(),
"sender": sendUser.toString(),
"mobile": userMobile.toString(),
"avatar": _image != null
? await MultipartFile.fromFile(_image.path,
filename: avatarName.toString())
: Text('Invalid Avatar'),
});
if (_validateAndSave()) {
final token = widget.token;
try {
Dio dio = Dio();
dio.options.headers['Accept'] = "application/json";
dio.options.headers['Authorization'] = "Bearer $token";
dio.options.headers['Content-Type'] = "multipart/form-data";
dio.options.followRedirects = false;
var response = await dio.post(url,
data: data, onSendProgress: (int rec, int total) {
setState(() {
uploading = true;
progressString = ((rec / total * 100).toString());
});
});
var responseCode = response.statusCode;
print('Dio responseCode : $responseCode');
} on DioError catch (err) {
var responseCode = err.response.statusCode;
print(responseCode);
}
setState(() {
uploading = false;
progressString = "100% Completed ";
print(progressString);
});
}
you should set your navigator inside of your upload function after upload finished
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
CircularProgressIndicator(
backgroundColor: Colors.blue,
),
SizedBox(
height: 20.0,
),
Text("Uploading File : $progressString",
style: TextStyle(
color: Colors.white54,
fontSize: 20.0,
fontWeight: FontWeight.w900,
),
),
],
),
FormData data = FormData.fromMap({
"username": userName.toString(),
"name": naMe.toString(),
"birthday": birthDay.toString(),
"about": aboutUser.toString(),
"sender": sendUser.toString(),
"mobile": userMobile.toString(),
"avatar": _image != null
? await MultipartFile.fromFile(_image.path,
filename: avatarName.toString())
: Text('Invalid Avatar'),
});
if (_validateAndSave()) {
final token = widget.token;
try {
Dio dio = Dio();
dio.options.headers['Accept'] = "application/json";
dio.options.headers['Authorization'] = "Bearer $token";
dio.options.headers['Content-Type'] = "multipart/form-data";
dio.options.followRedirects = false;
var response = await dio.post(url,
data: data, onSendProgress: (int rec, int total) {
setState(() {
uploading = true;
progressString = ((rec / total * 100).toString());
});
});
var responseCode = response.statusCode;
print('Dio responseCode : $responseCode');
} on DioError catch (err) {
var responseCode = err.response.statusCode;
print(responseCode);
}
Future.delaye(Duration(milliseconds: 100), (){
Navigator.pushReplacement(this.context,
MaterialPageRoute(builder: (context) {
return LoginPage();
}),
);
});
}
When the upload is complete, update the UI to show the user that the upload is complete and add a post frame callback to navigate to the next page.
Step 1 : Create a new file i.e, splashscreen.dart file under lib folder. In main.dart file give reference to SplashScreen().
Filename: main.dart
import 'package:flutter/material.dart';
import 'package:mfitz/splashscreen.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: SplashScreen(),
);
}
}
Step 2: Under splashscreen.dart file create the required UI for the splash screen and include the following code under void initState() method to navigate to the new screen after 5 seconds.
Timer(Duration(seconds: 5), () {
Navigator.of(context)
.pushReplacement(MaterialPageRoute(builder: (_) => MainScreen()));
Splash Screen Code
Filename: splashscreen.dart
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'mainScreen.dart';
class SplashScreen extends StatefulWidget {
#override
_SplashScreenState createState() => _SplashScreenState();
}
class _SplashScreenState extends State<SplashScreen> {
#override
void initState() {
super.initState();
//Navigates to new screen after 5 seconds.
Timer(Duration(seconds: 5), () {
Navigator.of(context)
.pushReplacement(MaterialPageRoute(builder: (_) => MainScreen()));
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
fit: StackFit.expand,
children: [
Container(
constraints: BoxConstraints.expand(),
decoration: BoxDecoration(
image: new DecorationImage(
image: AssetImage('assets/images/img2.jpg'),
fit: BoxFit.fill,
),
),
),
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Expanded(
flex: 2,
child: Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
CircleAvatar(
backgroundColor: Colors.grey[100],
radius: 80.0,
child: Text(
"MOBIFIT.",
style: GoogleFonts.aldrich(
fontWeight: FontWeight.bold,
color: Colors.black,
fontSize: 30.0),
textAlign: TextAlign.center,
),
),
],
),
),
),
Expanded(
flex: 1,
child: Column(
children: [
Padding(
padding: EdgeInsets.only(right: 100.0, left: 100.0),
child: LinearProgressIndicator(
backgroundColor: Colors.white,
valueColor:
AlwaysStoppedAnimation<Color>(Colors.grey),
minHeight: 10.0,
),
),
Padding(padding: EdgeInsets.only(bottom: 10.0))
],
))
],
)
],
),
);
}
}
SPLASHSCREEN
MAINSCREEN
Image is not coming but all the data in the firebase database is showing in the app.
import 'package:flutter/material.dart';
import 'Authentication.dart';
import 'photoUpload.dart';
import 'Posts.dart';
import 'package:firebase_database/firebase_database.dart';
// import 'package:flutter_blogapp/Authentication.dart';
// import 'package:flutter_blogapp/photoUpload.dart';
class HomePage extends StatefulWidget
{
HomePage
(
{
this.auth,
this.onSignedOut,
}
);
final AuthImplementation auth;
final VoidCallback onSignedOut;
#override
State<StatefulWidget> createState()
{
return _HomePageState();
}
}
class _HomePageState extends State<HomePage>
{
List<Posts> postsList = [];
#override
void initState()
{
super.initState();
DatabaseReference postsRef = FirebaseDatabase.instance.reference().child("Posts");
postsRef.once().then((DataSnapshot snap)
{
var KEYS = snap.value.keys;
var DATA = snap.value;
postsList.clear();
for(var individualKey in KEYS)
{
Posts posts = new Posts
(
DATA[individualKey]['image'],
DATA[individualKey]['description'],
DATA[individualKey]['data'],
DATA[individualKey]['time'],
);
postsList.add(posts);
}
setState(()
{
print('Length : $postsList.length');
});
});
}
void _logoutUser() async
{
try
{
await widget.auth.signOut();
widget.onSignedOut();
}
catch (e)
{
print(e.toString());
}
}
#override
Widget build(BuildContext context)
{
return new Scaffold
(
appBar: new AppBar
(
title: new Text('Home'),
),
body : new Container
(
child: postsList.length == 0 ? new Text(" No Post available ") : new ListView.builder
(
itemCount: postsList.length,
itemBuilder: (_, index)
//itemBuilder: (BuildContext _, int index ) //<-----
{
return PostsUI(postsList[index].image, postsList[index].description, postsList[index].date, postsList[index].time);
}
),
),
bottomNavigationBar: new BottomAppBar
(
color: Colors.pink,
child: new Container
(
margin: const EdgeInsets.only(left: 70.0, right: 70.0),
child: new Row
(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
mainAxisSize: MainAxisSize.max,
children: <Widget>
[
new IconButton
(
icon: new Icon(Icons.local_car_wash),
iconSize: 50,
color: Colors.white,
onPressed: _logoutUser,
),
new IconButton
(
icon: new Icon(Icons.add_a_photo),
iconSize: 50,
color: Colors.white,
onPressed: ()
{
Navigator.push
(
context,
MaterialPageRoute(builder: (context)
{
return new UploadPhotoPage();
})
);
},
),
],
),
),
),
);
}
// Designing Posts UI
Widget PostsUI(String image, String description, String date, String time)
{
return new Card
(
elevation: 10.0,
margin: EdgeInsets.all(15.0),
child: new Container
(
padding: new EdgeInsets.all(14.0),
child: new Column
(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>
[
new Row
(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>
[
new Text
(
date,
style: Theme.of(context).textTheme.subtitle,
textAlign: TextAlign.center,
),
new Text
(
time,
style: Theme.of(context).textTheme.subtitle,
textAlign: TextAlign.center,
), //<----
],
),
SizedBox(height: 10.0,),
new Image.network(image, fit:BoxFit.cover),
SizedBox(height: 10.0,),
new Text
(
description,
style: Theme.of(context).textTheme.subhead,
textAlign: TextAlign.center,
),
],
)
)
);
}
}
The following errors are showing:
═══════ Exception caught by image resource service
════════════════════════════ The following ArgumentError was thrown
resolving an image codec: Invalid argument(s): No host specified in
URI file:///image
When the exception was thrown, this was the stack
0 _HttpClient._openUrl (dart:_http/http_impl.dart:2276:9)
1 _HttpClient.getUrl (dart:_http/http_impl.dart:2197:48)
2 NetworkImage._loadAsync package:flutter/…/painting/_network_image_io.dart:84
3 NetworkImage.load package:flutter/…/painting/_network_image_io.dart:47
4 ImageProvider.resolve...
package:flutter/…/painting/image_provider.dart:327 ... Image provider:
NetworkImage("image", scale: 1.0) Image key: NetworkImage("image",
scale: 1.0)
The main part of the error message is:
Invalid argument(s): No host specified in URI file:///image
From this it looks like you're storing a local file:/// reference into the database, instead of a globally accessible URL (typically starting with http:// or https://).
The solution is in the code that writes to the database, to actually upload the image to a public storage place (such as to Firebase Storage) and then write the resulting download URL into the database.
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