How to add a new html element through widget? - flutter

I'm trying to find a widget that can insert new video html.
It is for flutter web. I have javascript running in a website. Now I'm trying to reuse the javascipt in a website using flutter. dart:js package is used to invoke JS functions. Those JS functions manipulate two video element in a web page. So I need to create a widget that inserts video element. Below is the code for the video widget.
class ElementWidget extends SingleChildRenderObjectWidget {
.....
#override
RenderHTMLElement createRenderObject(BuildContext context) {
return RenderHTMLElement(tagName,properties);
}
}
class RenderHTMLElement extends RenderProxyBox {
......
#override
void paint(PaintingContext context, Offset offset) {
if (child != null) {
context.paintChild(child, offset);
}
StringBuffer buffer = StringBuffer();
if (_properties != null) {
for (String key in _properties.keys) {
buffer.write(" '$key'='${_properties[key]}'");
}
}
ParagraphBuilder builder = ParagraphBuilder(null);
builder.addText("<$_tagName ${buffer.toString()}></${_tagName}>");
context.canvas.drawParagraph(builder.build(), offset);
}
}
In web source, video element is not there.
Any idea on how to archive it?

Related

Flutter using Provider - context.watch<T>() for specific items in a list and ignores the other items updates

I am a newbie in Flutter and I am trying to build an app using Provider. I will try to provide an oversimplified example here. My app includes a model of a room.
class Room {
String roomDisplayName;
String roomIdentifier;
Image image;
List<IDevices> devices = [];
Room(this.roomDisplayName, this.roomIdentifier, this.image, this.devices);
}
Rooms have list of devices like a temperature sensor
class TempSensor implements IDevices {
late String tempSensorName;
late double temperatureValue;
late double humidityValue;
late int battery;
TempSensor(this.displayName, this.zigbeeFriendlyName);
UpdateTempSensor(double temperature, double humidiy, int battery) {
this.temperatureValue = temperature;
this.humidityValue = humidiy;
this.battery = battery;
}
I have a RoomProvider class that implements ChangeNotifier that is responsible for updating devices in List<Room> rooms
class RoomsRepositoryProvider with ChangeNotifier {
List<Room> get rooms {
//return _rooms;
return _rooms;
}
UpdateTemperatureSensor(TempSensor tempSensor) {
TempSensor? foundTempSensor = null;
_rooms.forEach((room) {
room.devices.forEach((element) {
if (element.displayName == tempSensor.displayName) {
foundTempSensor = element as TempSensor;
}
});
});
if (foundTempSensor != null) {
foundTempSensor?.UpdateTempSensor(tempSensor.temperatureValue,
tempSensor.humidityValue, tempSensor.battery);
notifyListeners();
}
}
I also have a Stateful widget page to show Room information like temperature/humidity value.
class DetailPage extends StatefulWidget {
final Room room;
DetailPage({required this.room});
#override
_DetailPageState createState() => _DetailPageState();
}
class _DetailPageState extends State<DetailPage> {
#override
Widget build(BuildContext context) {
context.watch<RoomsRepositoryProvider>().rooms;
return Text ("Temperature is ${widget.room.devices[0].temperatureValue}");
}
Here is question:
The problem I am facing is that, if I am showing the Living Room in DetailPage and the temperature sensor from Bedroom gets updated in the List<Room> rooms, the whole DetailPage gets rebuild. Since it is not an issue in the flutter and the app works good. I would still like to know how to solve this architecture problem, that the DetailPage only gets build for the room updates related to the room being shown?
PS: please ignore any build, indentation or naming convention mistakes.
To only rebuild the specific widget, you can wrap that widget inside Consumer widget provider by Provider in flutter. Consumer takes a builder function and will build the widget returned by this builder function only when the data changes.
Consumer(
builder:(context,_,__){
return Container();
},
),
To implement this, you can use a Comsumer widget
Consumer<RoomsRepositoryProvider>(
builder:(context,value,child) => Text("Temperature is ${value.room}");
),
A StatelessWidget is also sufficient. Don't forget the index by room. It should work like this
So, I solved my problem by creating a separate provider DevicesProvider that contains the list of devices modified in the room. I provide the current room by calling the method SetCurrentRoom(String currentRoomIdentifier) from the DetailPage and the provider does its job whenever the devices list in the current room updates.
class DevicesProvider with ChangeNotifier {
String _currentRoomIdentifier = "";
List<IDevices> _listCurrentRoomDevices = [];
List<IDevices> get ListCurrentRoomDevices => _listCurrentRoomDevices;
void SetCurrentRoom(String currentRoomIdentifier) {
_currentRoomIdentifier = currentRoomIdentifier;
}
UpdateDevicesList(IDevices device) {
if (serviceLocator<RoomProviderService>()
.rooms
.any((room) => room.roomIdentifier == _currentRoomIdentifier)) &&
IsDeviceUpdateComingFromCurrentRoom(device)
{
_listCurrentRoomDevices.clear();
var devices = serviceLocator<RoomProviderService>()
.rooms
.firstWhere(
(room) => room.roomIdentifier == _currentRoomIdentifier)
.devices;
_listCurrentRoomDevices.addAll(devices);
notifyListeners();
}
}
bool IsDeviceUpdateComingFromCurrentRoom(IDevices device) {
bool isUpdateFromCurrentRoom = false;
if (device.Name.contains(_currentRoomIdentifier)) {
isUpdateFromCurrenRoom = true;
}
return isUpdateFromCurrentRoom;
}
}
Maybe this can be solved in a different way which is more elegant or efficient, but for now my problem is solved with this approach.

In Flutter, navigating to a page that have a heavy widget is too slow in an old device environmnet

I'd like to implement Survey Platform
A survey has many questions and each of them has a part (ex. Part1, Part2, and so on...)
Each part is displayed in one page. (One page equals to one page)
After user finished the survey, they could check the result in one page. That page has a SingleChildScrollView and the SingleChildScrollView contains all of the question's answers. (This is a client's request, so it could not be revised)
Also, I have selected GetX library to administrate State and used RxDart to manage async.
At the onInit method in a answers page's controller, I call the result api and save it to RxList variable.
In answers page's view, if the RxList variable's value is null, it builds CircularProgressIndicator. And in a case of not null, it builds SingleChildScrollView.
I'd like to make the answers page right away pop up when the Get.toNamed method is called and the CircularProgressIndicator has been displayed until the RxList variable is initialized.
But, when I called the Get.toNamed method, the screen had been stoped in the page that called Get.toNamed method. And a few seconds later, the answers page finally pop up.
What should I do to solve this problem?? My code is like belows.
// Survey Answers View
class SurveyResultDetailView extends BaseView<SurveyResultDetailController> {
#override
PreferredSizeWidget? appBar(BuildContext context) {
#override
Widget body(BuildContext context) {
return Obx(
() {
if (controller.surveyDiagnosisUiModel == null) {
return CircularProgressIndicator();
}
return SingleChildScrollView(
child: Column(
children: [
So long childrens~~~
],
),
);
}
);
}
}
// Survey Answers Controller
class SurveyResultDetailController extends BaseController {
final PreferenceManager _preferenceManager =
Get.find(tag: (PreferenceManager).toString());
late final String surveyConfigId;
final Rxn<SurveyDiagnosisResultUiModel> _rxModel =
Rxn<SurveyDiagnosisResultUiModel>(null);
SurveyDiagnosisResultUiModel? get surveyDiagnosisUiModel => _rxModel.value;
void setSurveyDiagnosisUiModel(SurveyDiagnosisResultUiModel? value) {
_rxModel.value = value;
}
final RxList<SurveyQuestionListUiModel> _surveyQuestionListUiModel =
RxList<SurveyQuestionListUiModel>();
List<SurveyQuestionListUiModel> get surveyQuestionListUiModel =>
_surveyQuestionListUiModel.toList();
void getDiagnosisResultUiModel() {
Future<String> diagnosisResultFuture =
_preferenceManager.getString('recent_detail_result');
callDataService(
diagnosisResultFuture,
onSuccess: _handleDiagnosisResultResponseSuccess,
);
}
void _handleDiagnosisResultResponseSuccess(String response) {
~~~~~ response entity mapping to ui model code
_surveyQuestionListUiModel.refresh();
}
List<SurveyQuestionListUiModel> parseQuestionListByPartNumber(int number) {
return surveyQuestionListUiModel
.where((element) => element.partNumber == number)
.toList();
}
/// ------------> 생애설계 행동 진단 결과 관련 값
#override
void onInit() {
surveyConfigId = Get.arguments as String;
getDiagnosisResultUiModel();
super.onInit();
}
}

Get HTML element with web scraping on flutter

i´m trying to get the link of the video of this page with the package "web_scraper". I can find it using Chrome´s tools (is an attribute of a jw-video jw-reset element. The problem is that when you enter the page you can´t see this attribute before clicking in the play button. Can any one help me?
I have already this code:
import 'package:flutter/material.dart';
import 'package:web_scraper/web_scraper.dart';
class Prueba extends StatefulWidget {
#override
State createState() => new _Prueba();
}
class _Prueba extends State<Prueba> {
final webScraper = WebScraper('https://www3.animeflv.net');
List<Map<String, dynamic>> link;
void fetchProducts() async {
// Loads web page and downloads into local state of library
if (await webScraper
.loadWebPage('/ver/sword-art-online-alicization-war-of-underworld-20')) {
setState(() {
link = webScraper.getElement(
'div.jw-wrapper.jw-reset > div.jw-media.jw-reset > video.jw-video.jw-reset', ['src']);
});
}
}
#override
void initState() {
super.initState();
// Requesting to fetch before UI drawing starts
fetchProducts();
}
#override
Widget build (BuildContext ctxt) {
print(link);
return Container();
}
}

Is there a way to detect touch in pdf using flutter?

I'm using dart pdf library and I want to detect touch on screen while the pdf is being viewed. Is there a way to do that? For viewing the pdf file I'm using a PDFViewerScaffold with the created file's path. I tried wrapping the PDFViewerScaffold with Listener and GestureDetector, but no luck.
My code so far:
Viewing the pdf file:
class PdfViewerPage extends StatelessWidget {
final String path;
const PdfViewerPage({Key key, this.path}) : super(key: key);
#override
Widget build(BuildContext context) {
return PDFViewerScaffold(
path: path,
);
}
}
Making the pdf file:
final Document pdf = Document();
...
pdf.addPage(MultiPage(
pageFormat:
...
footer: (Context context) {
...
},
build: (Context context) => <Widget>[
...
...
Any help would be appreciated!
I had the same problem a while back, FlutterFullPdfViewer uses native component and encapsulate it in a FrameLayout(in android) that can only be manipulated through native code.
What I did is that I forked the project, and added my own implementation.
In the android part you have a method called in FlutterFullPdfViewerManager.java :
void openPDF(String path) {
File file = new File(path);
pdfView.fromFile(file)
.enableSwipe(true)
.swipeHorizontal(false)
.enableDoubletap(true)
.defaultPage(0)
.load();
}
You can change this to:
void openPDF(String path) {
File file = new File(path);
pdfView.fromFile(file)
.enableSwipe(true)
.swipeHorizontal(false)
.enableDoubletap(true)
.onTap(e-> {
if (e.getRawX() > 300) {//put your value here
//send rebuild instruction to flutter
}
.defaultPage(0)
.load();
}
Or something along these lines, you will also find that there is a lot of extra stuff that you can do on pdfView that could be helpful to you.

Change state in one Widget from another widget

I'm programming a flutter application in which a user is presented with a PageView widget that allows him/her to navigate between 3 "pages" by swiping.
I'm following the setup used in https://medium.com/flutter-community/flutter-app-architecture-101-vanilla-scoped-model-bloc-7eff7b2baf7e, where I use a single class to load data into my model, which should reflect the corresponding state change (IsLoadingData/HasData).
I have a main page that holds all ViewPage widgets. The pages are constructed in the MainPageState object like this:
#override
void initState() {
_setBloc = SetBloc(widget._repository);
_notificationBloc = NotificationBloc(widget._repository);
leftWidget = NotificationPage(_notificationBloc);
middleWidget = SetPage(_setBloc);
currentPage = middleWidget;
super.initState();
}
If we go into the NotificationPage, then the first thing it does is attempt to load data:
NotificationPage(this._notificationBloc) {
_notificationBloc.loadNotificationData();
}
which should be reflected in the build function when a user directs the application to it:
//TODO: Consider if state management is correct
#override
Widget build(BuildContext context) {
return StreamBuilder<NotificationState>(
stream: _notificationBloc.notification.asBroadcastStream(),
//initialData might be problematic
initialData: NotificationLoadingState(),
builder: (context, snapshot) {
if (snapshot.data is NotificationLoadingState) {
return _buildLoading();
}
if (snapshot.data is NotificationDataState) {
NotificationDataState state = snapshot.data;
return buildBody(context, state.notification);
} else {
return Container();
}
},
);
}
What happens is that the screen will always hit "NotificationLoadingState" even when data has been loaded, which happens in the repository:
void loadNotificationData() {
_setStreamController.sink.add(NotificationState._notificationLoading());
_repository.getNotificationTime().then((notification) {
_setStreamController.sink
.add(NotificationState._notificationData(notification));
print(notification);
});
}
The notification is printed whilst on another page that is not the notification page.
What am i doing wrong?
//....
class _SomeState extends State<SomeWidget> {
//....
Stream<int> notificationStream;
//....
#override
void initState() {
//....
notificationStream = _notificationBloc.notification.asBroadcastStream()
super.initState();
}
//....
Widget build(BuildContext context) {
return StreamBuilder<NotificationState>(
stream: notificationStream,
//....
Save your Stream somewhere and stop initialising it every time.
I suspect that the build method is called multiple times and therefore you create a new stream (initState is called once).
Please try let me know if this helped.