Flutter-web Webview use multiple LocalStorage management - flutter

Hello i do have a problem using Webviews in my Webapp
case use :
Dashboard, which is loading multiple Webviews where each Webview with each local storage is merged with the main local storage.
first problem :
i had to use the easywebview package to solve my loading webviews issues of a webview on chrome, but i would like to use webview package.
second problem :
Whene i used my chrome app with an easywebview loading inside of it i had two localstorage running independantly i wish that i can merge all my localstorages in the dashboard one.
code sample
import 'package:easy_web_view/easy_web_view.dart';
class LoginFrameWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
height: double.infinity,
width: double.infinity,
child: EasyWebView(
src: "http://localhost:5111/",
isHtml: false,
isMarkdown: false,
convertToWidgets: false,
onLoaded: () {},
));
}
}
Image of the localstorages:
localstorages
Solution needed:
make one main LocalStorage
using webviews instead of easywebview (if possible)

after some research i ended up with a different solutions:
//setup iframe
_iframeElement.height = '500';
_iframeElement.width = '500';
//listen to iframe (window.post.message)
window.onMessage.listen((event) {
print(event.data);
//some localstorage logic
});
_iframeElement.src = 'path';
_iframeElement.style.border = 'none';
// ignore: undefined_prefixed_name
ui.platformViewRegistry.registerViewFactory(
'iframeElement',
(int viewId) => _iframeElement,
);
_iframeWidget = HtmlElementView(
key: UniqueKey(),
viewType: 'iframeElement',
);
and than i just passed the collected data on queries
if there is a better solution i would listen to that.

Related

Generic filter Flutter

Goodmorning,
I'm developing an app with flutter but I'm facing some problems with Provider (I think something miss in my knowledge).
My app fetch data from my API and displays them in listview.
In whole app I have different screen which displays different data type in listview and now I want to create filtering logic.
To avoid rewrite same code multiple times I thought to create one screen to reuse for filtering purposes but I'm facing problems with state management.
What I did:
create base model for filter information
`
enum FilterWidget { TEXT_FIELD, DROPDOWN } //to resolve necessary Widget with getWidget() (to implement)
class FilterBaseModel with ChangeNotifier {
String? value= 'Hello';
FilterWidget? widgetType;
FilterBaseModel(this.value, this.widgetType);
onChange() {
value= value== 'Hello' ? 'HelloChange' : 'Hello';
notifyListeners();
}
}
`
One screen for display filters depending on request
List<FilterBaseModel> filters = [];
FilterScreen() {
//Provided from caller. Now here for test purposes
filters.add(FilterBaseModel('Filter1', FilterWidget.TEXT_FIELD));
filters.add(FilterBaseModel('Filter2', FilterWidget.TEXT_FIELD));
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: SafeArea(
minimum: EdgeInsets.symmetric(vertical: 15, horizontal: 15),
child: SingleChildScrollView(
child: Container(
height: 400,
child: Column(
children: filters
.map(
(e) => Consumer<FilterBaseModel>(
builder: (_, filter, child) =>
ChangeNotifierProvider.value(
value: filter,
child: CustomTextField(
`your text` initialText: e.value,
onTap: () {
e.onChange();
filter.onChange();
},
),
),
),
)
.toList(),
))),
),
);
}
`
The problem is in Consumer and ChangeNotifier.value.
Screen works quite well: widget are displayed and callback are called, what is wrong? I need to use onChange method of both instance to update UI otherwhise method was called but widget is not rebuilt.
I know that probably putting consumer there is not right way but I tried also to put outside but doesn't work.
I expect to have one filter screen which receives in input filters list information, display them, handle their state managment and return their value.
P.S: this code now works, but I know is not the right way
Thank you for help!
EDIT
Have same behaviour without ChangeNotifierProvider.value. Therefore I'm more confused than before because still persist the double call to onChange for correct rebuilding.
More bit confused about ChangeNotifierProvider.value using...

Flutter apps and web adaptive UI layout

I have been working on flutter mobile apps, already released multiple version to AppStore/PlayStore.
The code is built for mobile app design.
I am currently looking to support website using the same codebase.
One of the issue with supporting both mobile apps and web is that the UI layout is different.
For example: We will have top bar actions in web but bottom bar navigation in mobile apps.
I think I can use kIsWeb like below to create different appBar and bottomNavigationBar
for each Scaffold widget in each screen.
if (kIsWeb){
\\ web code
}
else{
\\ app code
}
What is the best strategy to build adaptive UI which works for mobile apps and website using same codebase?
Modify this according to your use case :)
1.) Define constraints
const mobileWidth = 480;
const tabletWidth = 900;
const desktopWidth = 1180;
2.) Create a Responsive widget which change layout according to screen size
class ResponsiveLayout extends StatelessWidget {
const ResponsiveLayout({
Key? key,
this.mobileView,
this.tabletView,
this.desktopView,
}) : super(key: key);
final Widget? mobileView;
final Widget? tabletView;
final Widget? desktopView;
#override
Widget build(BuildContext context) {
return LayoutBuilder(builder: (context, dimens) {
if (dimens.maxWidth <= tabletWidth) {
if (dimens.maxWidth <= mobileWidth) {
return mobileView ?? Text("Mobile view");
} else {
return tabletView ?? Text("Tablet view");
}
} else {
return desktopView ?? Text("Desktop view");
}
});
}
}
3.) Use this responsive widget where you want
class CourseScreen extends StatelessWidget {
const CourseScreen({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return const ResponsiveLayout(
mobileView: CourseMobileScreen(),
tabletView: CourseTabletScreen(),
desktopView: CourseDesktopScreen(),
);
}
}
Most likely UI depends on screen size rather than it is running on web or not. The web page can be resized and needed to maintain UI. Mostly I prefer using LayoutBuilder for responsiveness. You can also find some good package on pub. While there are some different functionality/feature depends on between os app/ web app, in this case I use kIsWeb. A web app can be used by android browser.
You can check more about adaptive-responsive design.
You should try responsive_framework pkg. I have used it in my Single code base and created different screen resolution breakpoints as per my use cases.
For ex.
builder: (context, widget) => ResponsiveWrapper.builder(
BouncingScrollWrapper.builder(context, widget),
maxWidth: MediaQuery.of(context).size.width/3,
minWidth: 450,
defaultScale: true,
breakpoints: [
ResponsiveBreakpoint.resize(450, name: MOBILE),
ResponsiveBreakpoint.autoScale(800, name: TABLET),
ResponsiveBreakpoint.autoScale(1000, name: TABLET),
ResponsiveBreakpoint.resize(1200, name: DESKTOP),
ResponsiveBreakpoint.autoScale(2460, name: "4K"),
],
background: Container(color: Color(0xFFF5F5F5))
),
Accordingly, use breakpoints for your UI.
Or
You can create your own screen configs using MediaQuery in a separate file e.g., SizeConfig
For ex.
For Mobile > max_width x maxheight can be 300 x 480. likewise for Tablet and Desktop.
Then you can use it to inflate list items in GridView (for crossAxisCount) and ListView items

How to check if screen is fully loaded?

Problem: One of screen in my app has bunch of network images that are being fetched from an API. When user navigates to that screen some images load faster than others, hence users sees a screen that is not fully loaded.
Expected behaviour: I want to show a CircularProgressIndicator until all the network images are fully loaded.
P.S. Below code doesn't do what I wanted, as it executes the function while images are still loading.
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) => yourFunction(context));
}
Also I am using SvgPicture.network from flutter_svg package.
final Widget networkSvg = SvgPicture.network(
'https://site-that-takes-a-while.com/image.svg',
semanticsLabel: 'A shark?!',
placeholderBuilder: (BuildContext context) => Container(
padding: const EdgeInsets.all(30.0),
child: const CircularProgressIndicator()),
);
Maybe this could help Flutter image preload

Flutter IFrameElement does not change source

I have created the following stateless widget to display some web contents in my Flutter WPA.
Its parent is state-full and rebuilds this widget when the _frameUrl is changed.
But this happens only the first time. After that when the parent changes _frameUrl I can see in the logs that the widget is getting rebuilt and has got the new URL, but it still reloads with the old URL given the first time.
import 'package:flutter/material.dart';
import 'dart:html';
import 'package:universal_ui/universal_ui.dart';
class HtmlFrame extends StatelessWidget {
var _iframeElement;
final String _frameUrl;
HtmlFrame(this._frameUrl);
#override
Widget build(BuildContext context) {
print("Frame build: $_frameUrl");
_iframeElement = IFrameElement();
_iframeElement.height = '500';
_iframeElement.width = '500';
_iframeElement.src = _frameUrl;
_iframeElement.style.border = 'none';
_iframeElement.allowFullscreen = true;
// ignore: undefined_prefixed_name
ui.platformViewRegistry.registerViewFactory(
'iframeElement',
(int viewId) => (_iframeElement as IFrameElement),
);
return HtmlElementView(
key: UniqueKey(),
viewType: 'iframeElement',
);
}
}
Here what the log looks like:
### First URL: https://www.google.com
Frame build: http://www.google.com
Refused to display 'https://www.google.com/' in a frame because it set 'X-Frame-Options' to 'sameorigin'.
### After giving it the second URL: https://www.bing.com
Frame build: https://www.bing.com/
Refused to display 'https://www.google.com/' in a frame because it set 'X-Frame-Options' to 'sameorigin'.
I used google and bing for the above log samples to show how it again tries to load the first URL, for the sameorigin error logs the URL it tried. In my app there is not such problem and the URLs belong to a allowed website.
Just have to give dynamic name while register you src url to ui. So best method and easy one is just pass url as register name ..In your code.
ui.platformViewRegistry.registerViewFactory(
_frameUrl,
(int viewId) => (_iframeElement as IFrameElement),
);
return HtmlElementView(
key: UniqueKey(),
viewType: _frameUrl,
);
That's it.

Flutter: setState((){} fails to redraw DropdownButton value

I am new to flutter, and am trying to code mobile game. So far I have been able to manage through previous questions on forums, youtube tutorials and example apps. However I have hit a wall that I cannot solve.
Many of the features in my UI are supposed to change based on user behavior but do not update, I am using this DropdownButton as my example but it is a problem I am having elsewhere in the code as well. When the user makes a selection from the DropdownButton it should update the value of the button, but instead only the hint is displayed. I have been able to determine through print statements that the selection is registered.
Based on what I have learned so far I suspect that the issue entails my widget's statefulness or lack thereof. I found a few similar questions which suggested using a StatefulBuilder, however when I tried to implement it the code had errors or else duplicated my entire ListTile group. I also saw suggestions about creating a stateful widget instead but the instructions were too vague for me to follow and implement without errors. I am including snippets of the code below, I am hesitant to paste the whole thing because the app is nearly 2000 lines at this point. Please let me know if any further information or code is needed.
this is the problematic code, see below for the code in context.
DropdownButton(
value: skillChoice,
items: listDrop,
hint: Text("Choose Skill"),
onChanged: (value) {
setState(() {
skillChoice = value;
});
},
),
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Hunters Guild',
theme: ThemeData(
primaryColor: Colors.grey,
),
home: Main(),
);
}
}
final List<Combatant> _combatants = List<Combatant>();
//combatant is a class that holds stats like HP which may change during a battle
//they also have a Fighter and a Monster and one of those will be null and the other useful
//depending if faction = "good" or "evil
//I am aware that this may not be the most elegant implementation but that is a problem for another day.
class MainState extends State<Main> {
final List<Fighter> _squad = List<Fighter>();
//Fighter is a class which stores info like HP, name, skills, etc for game character,
//this List is populated in the page previous to _fight
//where the user picks which characters they are using in a given battle
void _fight(Quest theQuest){
for(Fighter guy in _squad){
_combatants.add(Combatant("good", guy, null));
}
_combatants.add(Combatant("evil", null, theQuest.boss));
//quests are a class which store a boss Monster, later on I will add other things like rewards and prerequisites
final tiles = _combatants.map((Combatant comba){ //this structure is from the build your first app codelab
List<DropdownMenuItem<String>> listDrop = [];
String skillChoice = null;
if (comba.faction == "good"){
for (Skill skill in comba.guy.skills){
listDrop.add((DropdownMenuItem(child: Text(skill.name), value: skill.name)));
}
}
else if (comba.faction == "evil"){
for (Skill skill in comba.boss.skills){
listDrop.add((DropdownMenuItem(child: Text(skill.name), value: skill.name)));
}
}
//^this code populates each ListTile (one for each combatant) with a drop down button of all their combat skills
return ListTile(
leading: comba.port,
title: Text(comba.info),
onTap: () {
if(comba.faction == "good"){
_fighterFightInfo(comba.guy, theQuest.boss); //separate page with more detailed info, not finished
}
else{
_monsterFightInfo(theQuest.boss); //same but for monsters
}
},
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
DropdownButton( //HERE IS WHERE THE ERROR LIES
value: skillChoice,
items: listDrop,
hint: Text("Choose Skill"),
onChanged: (value) {
setState(() {
skillChoice = value;
});
},
),
IconButton(icon: Icon(Icons.check), onPressed: (){
//eventually this button will be used to execute the user's skill choice
})
],
),
subtitle: Text(comba.hp.toString()),
);
},
);
Navigator.of(context).push(
MaterialPageRoute<void>(
builder: (BuildContext context) {
//I have tried moving most of the above code into here,
//and also implementing StatelessBuilders here and other places in the below code to no effect
final divided = ListTile.divideTiles(
context: context,
tiles: tiles,
).toList();
return Scaffold(
appBar: AppBar(
title: Text("Slay "+theQuest.boss.name+" "+theQuest.boss.level.toString()+" "+theQuest.boss.type.name, style: TextStyle(fontSize: 14),),
leading: IconButton(icon: Icon(Icons.arrow_back), onPressed: () {
_squadSelect(theQuest);
}),
),
body: ListView(children: divided),
);
},
),
);
}
//there are a lot more methods here which I haven't included
#override
Widget build(BuildContext context) {
//I can show this if you need it but there's a lot of UI building on the main page
//Also might not be the most efficiently implemented
//what you need to know is that there are buttons which call _fight and go to that page
}
class Main extends StatefulWidget {
#override
MainState createState() => MainState();
}
Thank you for any help or advice you can offer.
Its a bit difficult to understand what is going on here, so if possible add the problem areas in a new widget and post it here.
In any case, for now it seems your skillShare variable is local, so every time you are setting state it does not update. Try something like this:
class MainState extends State<Main> {
String skillShare = ""; //i.e make skillShare a global variable
final List<Fighter> _squad = List<Fighter>();
The following question had information which helped me find the answer. Different question but the solution applies. Thanks user:4576996 Sven. Basically I need to reformat from doing my constructing in the void _fight to a new stateful page. This may be useful for anyone else who is using the beginning tutorial as a framework to build their app.
flutter delete item from listview