I am using Flutter_bloc package to make a phone auth in flutte, everything work good, but my question is about adding events to the bloc, for example in my application, when i click on button like this code below, the event added to my loginBloc, and everything works good, but when i press back button in android device, and then return back by using normal navigater.pushNamed, and click the button again nothing happen? that mean the event not added to bloc or something like this? can anybody explain this problem? thanks in advance: this is my sample code to add event when click button:
child: RaisedButton(
onPressed: () {
if (_formKey.currentState.validate()) {
loginBloc.add(LoginPressesEvent(
phoNo: _phoneTextController.value.text));
}
},
For adding an 'Event' to 'Bloc' use this code:
BlocProvider.of<'YourBlocClass'>('blocContext').add('YourEvent()'));
'blocContext' is context parameter of `listener in BlocListener' :
BlocProvider(
create: (context) => BlocClass()..add(Fetch()),
child: BlocListener<BlocClass, BaseState>(
listener: (listenerContext, state) {
// listenerContext: store this parameter to Field
// and use that everywhere in your StateClass
},
or context parameter of 'builder in Bloc Builder`
BlocProvider(
create: (context) => BlocClass()..add(Fetch()),
child: BlocBuilder<IndexBloc, BaseState>(
builder: (builderContext, state) {
// builderContext: store this parameter to Field
// and use that everywhere in your StateClass
},
Related
i have an app that renders a webpage inside a webview. this actually has a bunch of crypto addresses.
I want them to be automatically made clickable. and when they are clicked - i want to show a popup (some information about the addresses).
can this be done ?
im very unsure if this thing about changing the UI is possible...but in desktop web world, there are extensions that do this. if there are any examples of flutter webview codebases that do this, that would be helpful
the second point - communicating back and forth with the webpage is even more confusing. can this be done at all ? can i receive the data of the click back to main flutter app and then do something ?
Here's a working example
This is achieved as I said by Injecting Js when the webpage loads, as for communication with Flutter, I used the flutter Webview plugin provided JavascriptChannel
The Javascript code looks for a specific element firstly on Page load and secondly while scrolling the webpage (to account for newly created dynamic elements)
Here's how the flow works
JS: assigns the element a new css style (Or in your case make it look like a button) or even create a button and insert it into the webpage
JS: assign on click to the element to call the Flutter JS Channel.
Flutter: Receive message Display a snackbar - you can deeplink or do whatever you want.
As the comments on the JS code say. the scrolling behavior calls every time which is not always ideal, you can use another function make it only trigger on a specific scroll distance
Full working example
import 'dart:developer';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';
dynamic ctrl;
// if you have multiple elements, just use querySelector All and loop
const jsCodeToForAnElement = """
// Choose the element
watch();
// I would add dealy to calculate delta between scrolls
// Meaning this code wont watch on every scroll
window.onscroll = watch();
function watch(){
let elm = document.querySelector("main h1");
try{
// Style it
elm.style.cssText = "background: red";
// Add on click
elm.onclick = (event)=> {
var walletHTMLElement = event.target;
// Use native API to communicate with Flutter
jsMessager.postMessage('Wallet clicked: ' + walletHTMLElement.innerHTML);
};
}catch(e){
jsMessager.postMessage('Error: ' + e);
}
}
""";
void main() {
runApp(
MaterialApp(
home: WebViewExample(),
),
);
}
class WebViewExample extends StatefulWidget {
#override
WebViewExampleState createState() => WebViewExampleState();
}
class WebViewExampleState extends State<WebViewExample> {
#override
void initState() {
super.initState();
// Enable virtual display.
if (Platform.isAndroid) WebView.platform = AndroidWebView();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: WebView(
initialUrl: 'https://flutter.dev/',
debuggingEnabled: true,
onWebViewCreated: (WebViewController webViewController) {
ctrl = webViewController;
},
javascriptMode: JavascriptMode.unrestricted,
javascriptChannels: <JavascriptChannel>{
JavascriptChannel(
name: 'jsMessager',
onMessageReceived: (jsMessager) async {
if (jsMessager.message.contains("Wallet")) {
var snackBar = SnackBar(
content: Text(jsMessager.message),
);
ScaffoldMessenger.of(context).showSnackBar(snackBar);
// Do Other Thing like deeplinking to crypto app
// Deeplink to wallet LaunchApp(wallet) <-- clean wallet string first
}
}),
},
onPageStarted: (String url) async {
ctrl.runJavascript(jsCodeToForAnElement);
},
onPageFinished: (String url) async {},
),
);
}
}
Original Answer
This would be possible by Injecting Javascript into Webviews (This is one idea)
1 - I would wait for the page to load
2 - Modify the HTML content using Javascript
Should be pretty straight forward.
Refer to this Answer to see how it is done in Flutter.
https://stackoverflow.com/a/73240357/6151564
I'm trying to add a firebase analytics track screen to my app.
I tried to enter it in the menu like in the picture below but it always doesn't read
I've tried some code but it doesn't work. For now, the code I'm using is as follows:
FirebaseAnalyticsObserver(analytics: analytics);
onGenerateRoute: widget.appRouter.onGenerateRoute,
builder: EasyLoading.init(),
initialRoute: splashScreenRoute,
navigatorObservers: <NavigatorObserver>[
// FirebaseAnalyticsObserver(analytics: _analytics),
observer
]
and on each screen I add code like this below on each initState()
analytics.setCurrentScreen(screenName: 'Page Detail Mobil');
i have re-run the app but it doesn't work track screen and put in firebase analytic. please help me thank you
I have used different function to track my screens. I have called my logScreens function on routing or you can call it on initState of each pages.
This is my analytic_service.dart
class AnalyticsService {
final FirebaseAnalytics _analytics = FirebaseAnalytics();
Future logScreens({#required String? name}) async {
await _analytics.setCurrentScreen(screenName: name);
}
}
and this is locator.dart
import 'package:get_it/get_it.dart';
import 'analytic_service.dart';
GetIt locator = GetIt.instance;
void setupLocator() {
if (!locator.isRegistered<AnalyticsService>()) {
locator.registerLazySingleton(() => AnalyticsService());
}
}
Call the logScreens function when needed.
locator<AnalyticsService>().logScreens(name: "Dashboard");
And this will be logged in analytics like this.
Try this on DebugView. Click on the screen_view. It will show the screen like this.
I'm not using API or firebase yet, the data is stored locally.
here is my code
======>>
Widget _buildhouse(BuildContext contex, int index){
Size size = MediaQuery.of(context).size;
House house = houselist[index]; //houselist is the list of all houses
return GestureDetector(
onTap: (){
setState(() {]
house = filteredhouse[index]; //this code wont be executed
print(house.price);
});
Navigator.push(context, MaterialPageRoute(builder: (_) => DetailsScreen(house),));
},
so those two lines that I commented on are the important ones I guess, the print code gets executed but not the other one. also if I say "house = filteredhouse[index];" at the beginning, I will get the filtered value. but it won't get changed when clicked the button
solved it by using this function void _filtered(){ setState(() { houselist = filteredhouse; }); } and then calling it inside of setState
This is weird but below code is not working for me. I get a back arrow on the home screen when using below code.
First line below is for dismissing the dialog box. second one is to go to home screen.
Navigator.of(context, rootNavigator: true).pop();
Navigator.of(context).pushReplacementNamed(HomeScreen.id);
This is first time I am facing this kind of situation with pushReplacementNamed. what's going on here ?
It is probably because you have another screen in the stack. When you call pushReplacementNamed, it doesn't replace whole stack with the one you give. Can you try the following code;
// true don't work based on above query condition
Navigator.of(context).pushNamedAndRemoveUntil(HomeScreen.id, (Route<dynamic> route) => true);
// false works
Navigator.of(context).pushNamedAndRemoveUntil(HomeScreen.id, (Route<dynamic> route) => false);
That won't give the required result as you have already even popped the context away before calling another Navigator class. I tried the function Navigator.of(context).pushNamedAndRemoveUntil() but still got my HomeScreen pushed on stack twice with the back button on screen 1. Hence, I finally got this with the inbuilt function Navigator.of(context).popUntil(). You can run this dartpad code https://dartpad.dev/a10ed43452736b5c6b3d1abe6a7eda45 to view the desired effect or view the code below. Below is part of the code from the gist:
...
class ThirdPage extends StatelessWidget{
static const routeName = '/third';
#override
Widget build(BuildContext context){
void _nextPage(){
//Logic here - ***************************
Navigator.of(context).popUntil((Route<dynamic> route) => route.isFirst);
}
return Scaffold(
appBar: AppBar(
title: Text('Third Page'),
),
body: Center(
child: Text('Third Page'),
),
floatingActionButton: FloatingActionButton(
onPressed: _nextPage,
child: Icon(Icons.add),
),
);
}
}
Happy coding D:)
I used Navigator.push up to 6 screens to get to the payment page. After Payment, I want to push to the "Payment Successful" page then remove all the previous screens i.e using the back button will return to the very first screen.
NOTE: I have tried pushReplacementNamed and it doesn't work.
I figured it out. It was the Navigator.pushAndRemoveUntil function. Where i had to pass the PaymentSuccessful widget as the newRoute, and the "/Home" route as the predicate
_navPaymentSuccessful(){
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(
builder: (context) => PaymentSuccessful()
),
ModalRoute.withName("/Home")
);
}
Accepted Answer is correct. But you can try this too.
Navigator.pushAndRemoveUntil<dynamic>(
context,
MaterialPageRoute<dynamic>(
builder: (BuildContext context) => YourPageNameGoesHere(),
),
(route) => false,//if you want to disable back feature set to false
);
even simpler and I think a better way would be to do it this way,
this Schedules a callback for the end of the current persistent frame,to push to route /loginPage and removes all the previous routes,this way you can make sure that all the frames are rendered and then you navigate to next page.
SchedulerBinding.instance.addPostFrameCallback((_) {
Navigator.of(context).pushNamedAndRemoveUntil(
'/loginPage', (Route<dynamic> route) => false);
});
I would Suggest use WillPopScope in your Payment successful page and onWillPop method write following snippet of code:
return WillPopScope(
onWillPop: (){
Navigator.of(context)
.pushNamedAndRemoveUntil('/Home', (Route<dynamic> route) => false);
},
child: Scaffold()
};
Try this if you want pass arguments to new page:
Navigator.of(context).pushNamedAndRemoveUntil(
'/new-route-name',
arguments: {
any object
},
ModalRoute.withName("/the-route-name-that-you-want-back-to-it")
);