Iam new to flutter,tried hello_services example provided by flutter.io .
in that example both flutter view and native view are in same screen.
My question is, how to navigate to two different screens like one written in flutter and another in native(android/ios) with params or extras.Please help !!!! thanks
The only solution I found, it is to send a message to your native view (https://flutter.io/platform-services/), catch the message in Java or Swift/ObjectiveC code then navigate to the other view.
Dart Code
Map params = <String, dynamic>{
"view": "MyView"
};
PlatformMessages.sendJson("navigateTo", params);
Java Code
flutterView.addOnMessageListener("navigateTo", new FlutterView.OnMessageListener() {
#Override
public String onMessage(FlutterView view, String message) {
try {
JSONObject object = new JSONObject(message);
if (object.getString("view") == "MyView") {
// navigate to MyView
}
} catch (JSONException e) {
e.printStackTrace();
}
return null;
}
});
Related
When calling textfield on flutter in fireOS in the fire tv devices, to do a search for example the fireOS virtual keyboard pops on top of textfield and doesnt work like on other android devices where the keyboard is on the bottom and textfield is visible.
On android legacy for example i can use edittext widget and the same keyboard pops on top but whatever i type with the controller updates on the virtual keyboard itself, because the keyboard has its own textfield or edittext. So my problem is how could i update the edittext on firetv virtual keyboard with flutter.
Okay, so I didnt find an answer anywhere so I had to do some hacky stuff heres how i got it to work since flutter is a no go.
Solution Overview:
1.- So first check is you are running on Android, you can do this with if (Platform.isAndroid) on flutter.
2.- If you are actually running on android you can then open a platform channel to native android to check the actual manufacturer(I will post how to code below).
3.- Check manufacturer or device name for "Amazon" or "Kindle" or whatever an if(string.contains("")) will do the trick.
4.- Open again a platform channel to Native Android and open an Alert Dialog with an Edittext, capture the resulting string and return it to flutter.
And thats how i got firetv's keyboard to work under flutter.
if (Platform.isAndroid){
checkOs().then((String osName){
print("Device running on: $osName");
if(osName.contains("Amazon") || osName.contains("AFTN")){
fireTvKeyboardInput().then((String result){
buscarTitulo(result);
});
}else{
_showDialog(); // Keyboard for NON FIREOS devices on Android.
}
});
}else{
//IF Device is not Android Eg. IOS
_showDialog();
}
Now theres two functions i used "checkOs" and "fireTvKeyboardInput" heres the code:
Future<String> checkOs() async {
String myResult = "";
try {
myResult = await platform.invokeMethod("checkOS", <String, dynamic>{
'param1': "hello",
});
}catch (e){
print ("exception: $e");
}
return myResult;
}
Future<String> fireTvKeyboardInput() async {
String myResult = "";
try {
myResult = await platform.invokeMethod("fireKeyBoard", <String, dynamic>{
'param1': "hello",
});
}catch (e){
print ("exception: $e");
}
return myResult;
}
On Native Android heres the code:
if(call.method == "checkOS"){
val operatingSystem = android.os.Build.MANUFACTURER + "- " + android.os.Build.MODEL
result.success(operatingSystem)
}
if(call.method == "fireKeyBoard"){
val alert = AlertDialog.Builder(this)
alert.setMessage("Search")
// Set an EditText view to get user input
val input = EditText(this)
input.hint = "Enter Text"
input.inputType = InputType.TYPE_CLASS_TEXT
alert.setView(input)
input.setOnKeyListener { view, keyCode, keyEvent ->
if (keyCode == 66) {
val imm = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
imm.hideSoftInputFromWindow(input.windowToken, 0)
}
false
}
alert.setPositiveButton("Ok") { dialog, whichButton ->
result.success(input.text.toString());
}
alert.setNegativeButton("Cancel") { dialog, whichButton ->
// Canceled.
}
alert.show()
}
I have a Xamarin.Forms app and I am using FreshMvvm framework.
If I do this from ViewIsAppearing method of FirstPageModel:
CoreMethods.PushPageModel<SecondPageModel>();
I go the "SecondPageModel". Then, when I am in the "SecondPageModel" if I do:
CoreMethods.PopPageModel();
or press hard back button, or press title bar back button not works in Android (anything happens). I am using FreshMasterDetailNavigationContainer.
In iOS it works OK, I get back to FirstPageModel.
This is because ViewIsAppearing will always be called when the page starts displaying on the screen. When you pop the second page then go to the first page, the first page's ViewIsAppearing will fire again. It caused a dead cycle and prohibited your app from returning to the first page.
Add a property to avoid that:
bool isInitialized;
public FirstPageModel()
{
// ...
isInitialized = true;
}
protected async override void ViewIsAppearing(object sender, EventArgs e)
{
base.ViewIsAppearing(sender, e);
if (isInitialized)
{
await Task.Delay(100);
await CoreMethods.PushPageModel<SecondPageModel>();
isInitialized = false;
}
}
iOS may optimize this process, but I still recommend you to add this judgment statement.
Update:
Call it when your app has reached the main thread.
protected override void ViewIsAppearing(object sender, EventArgs e)
{
base.ViewIsAppearing(sender, e);
if (isInitialized)
{
Device.BeginInvokeOnMainThread(() =>
{
CoreMethods.PushPageModel<SecondPageModel>();
isInitialized = false;
});
}
}
I guess I do not understand how things like the "Back Button" affect how Activities behave.
Here is my main activity screen shot:
If the user clicks the bookmark icon in the upper right, my BookmarksActivity displays like so:
Here the user clicks the Add button to add the bookmark and I call finish() in my BookmarksActivity class and the screen returns to the my MainActivity ...
Now let's say the user wants to delete a bookmark, they would again click the bookmark icon which presents the user with this screen:
Now the user can click on the delete button resulting in this screen:
Now the user wants to get back to the MainActivity's screen by pressing the Back Button, doing so removes the keyboard as one would expect, resulting in this screen:
But now the user still wants to get back the main screen, so they click the Back Button again, but instead of the main screen one would expect to see they see this one!
Now there is no bookmark in my SQLite database yet one is being displayed. If the user clicks that back button again they do, finally, get the main activity screen ...
... and if they click on the Bookamrk icon you can see that there is no bookmark:
Thanks for bearing with me and that lengthy description of the problem. Here is what I believe to be the pertinent snippets of code:
Here's where the delete happens:
public class BookMarksBaseAdapter extends BaseAdapter {
...
#Override
public View getView(int position, View convertView, ViewGroup parent) {
// TODO Auto-generated method stub
View vi=convertView;
if(convertView==null)
vi = inflater.inflate(R.layout.bookmark, null);
TextView tv_bookmark_name = (TextView)vi.findViewById(R.id.bookmark_name);
TextView tv_bookmark_clock = (TextView)vi.findViewById(R.id.bookmark_clock);
Button deleteButton = (Button)vi.findViewById(R.id.btn_delete_bookmark);
final bookMark bookmark = new bookMark(data.get(position).get_bookmark_name(), data.get(position).get_bookmark_track(), data.get(position).get_bookmark_clock(), 0);
final String bookmark_name = bookmark.get_bookmark_name();
final int ibookmark_clock = bookmark.get_bookmark_clock();
// Setting all values in listview
tv_bookmark_name.setText(bookmark_name);
tv_bookmark_clock.setText(utils.milliSecondsToTimer(ibookmark_clock));
deleteButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Log.d( TAG, "delete button clicked line 73" );
db.deleteBookmark(bookmark);
v.getContext().startActivity(new Intent(v.getContext(), com.redcricket.myApp.BookMarksActivity.class));
}
});
return vi;
}
And here is the an snippet from my BookmarkActivity onCreate method:
#Override
public void onCreate(Bundle savedInstanceState) {
currentChapterTitle = com.redcricket.myApp.MainActivity.getTrackTitle(0);
currentTrack = com.redcricket.myApp.MainActivity.getCurrentSongIndex();
currentTrackPosition = "00:00:00";
db = new Databasehandler(this);
db.getWritableDatabase();
utils = new Utils();
try {
currentChapterTitle = com.redcricket.myApp.MainActivity.getCurrentTrackTitle();
} catch (Exception e) {
Log.d( TAG, "expection line 27" );
e.printStackTrace();
}
try {
icurrentTrackPosition = com.redcricket.myApp.MainActivity.getCurrentTrackPosition();
currentTrackPosition = utils.milliSecondsToTimer(icurrentTrackPosition);
} catch (Exception e) {
Log.d( TAG, "expection line 34" );
e.printStackTrace();
}
super.onCreate(savedInstanceState);
setContentView(R.layout.bookmarks);
try {
new_bookmark_name = (EditText) findViewById(R.id.new_bookmark_name);
} catch (Exception e) {
Log.d( TAG, "expection line 43" );
e.printStackTrace();
}
try {
new_bookmark_name.setText( currentChapterTitle );
} catch (Exception e) {
Log.d( TAG, "expection line 49" );
e.printStackTrace();
}
try {
new_bookmark_clock = (TextView) findViewById(R.id.new_bookmark_clock);
} catch (Exception e) {
Log.d( TAG, "expection line 55" );
e.printStackTrace();
}
try {
new_bookmark_clock.setText( currentTrackPosition );
} catch (Exception e) {
Log.d( TAG, "expection line 61" );
e.printStackTrace();
}
try {
addButton = (Button) findViewById(R.id.btn_add_new_bookmark);
} catch (Exception e) {
Log.d( TAG, "expection line 43" );
e.printStackTrace();
}
addButton.setOnClickListener(this);
bookMarkList = db.getAllBookmarks();
// add list
bookmark_list=(ListView)findViewById(R.id.bookmarks_list);
adapter=new BookMarksBaseAdapter(this, bookMarkList, this);
bookmark_list.setAdapter(adapter);
I must be doing something wrong somewhere. I have tried to override the onBackButton method and have it call finish but that didn't help at all. My best guess is that this line in wrong:
v.getContext().startActivity(new Intent(v.getContext(), com.redcricket.myApp.BookMarksActivity.class));
I call that when the delete button get pressed.
Any help welcomed. Thanks!
I am assuming Main is one activity, book mark is another activity and book mark delete is another activity. Unless you tell an activity to have no history or explicitly finish it before going to another activity it will stay in the activity stack.
In your example the user goes MainActivity -> BookMarkActivty then back to MainActivity through on back pressed which removed BookMarkActivity from the stack. Its all good.
In your other example the user goes MainActivity -> BookMarkActivity -> DeleteActivity
the question here is when they click delete are you finishing DeleteActivty or starting a new BookMarkActivty?
It looks like you are starting a new BookMarkActivty, finished the old BookMarkActivity and ended up with a stack of Main - DeleteBookMark - BookMark after the deletion process.
Don't finish bookmark when they choose to do a delete and set the delete activity to have no history or explicitly finish it after delete.
Doh! I figured out what I needed to do. I need to save the Activity that gets passed to the constructor of my BookMarksBaseAdapter class as a private member like so ...
public class BookMarksBaseAdapter extends BaseAdapter {
private Activity activity;
...
BookMarksBaseAdapter (Activity a, ArrayList<bookMark> d) {
activity = a;
data=d;
inflater = (LayoutInflater)activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
utils = new Utils();
db = new Databasehandler(a);
}
... then I call call activity.finish() when the delete button gets pressed like this ...
deleteButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
db.deleteBookmark(bookmark);
activity.finish();
}
});
In Eclipse you can use menu contributions to add toolbar buttons and menus that will call a command. Is there any way to do this to normal swt buttons, apart from programmatically calling the command onclick?
button.addSelectionListener(new SelectionAdapter() {
#Override
public void widgetSelected(SelectionEvent e) {
IHandlerService handlerService = (IHandlerService) getSite()
.getService(IHandlerService.class);
try {
handlerService.executeCommand("my command id", null);
} catch (Exception ex) {
throw new RuntimeException("command with id \"my command id\" not found");
}
}
});
No. You have to listen for the button event and the invoke the command programmatically.
You can use CommandContributionItems in a View or Wizard like that:
CommandContributionItemParameter param = new CommandContributionItemParameter(getSite(),
"myCommand", "com.voo.myCommand", CommandContributionItem.STYLE_PUSH);
param.label = "My Label";
CommandContributionItem item = new CommandContributionItem(param);
item.fill(parent);
I have actions like this:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult New(Product product)
{
try
{
if(ModelState.IsValid)
{
_productService.Create(product);
TempData["success"] = "Product created successfully!";
return RedirectToAction("Edit", new { product.Id });
}
}
catch (Exception e)
{
Logger.Exception(e);
TempData["error"] = "Oops, an error occurred! Please try again in a few moments.";
}
return View(product);
}
I want to take this error handling logic out of the methods. However, instead of the default [HandleError] way of doing things, instead of redirecting the user to another view in case of an error, it returns me the same view with a TempData["error"], and a notification will appear in the top of the same page.
How could I do this, delete all this try{}catch{} code and put this logic outside this action, for other actions as well?
You should be able to use [HandleError] and OnException to do what you want. Eg, to display a custom view:
protected override void OnException(ExceptionContext filterContext)
{
// Output a nice error page
if (filterContext.HttpContext.IsCustomErrorEnabled)
{
filterContext.ExceptionHandled = true;
this.View("Error").ExecuteResult(this.ControllerContext);
}
}
I posted a little more info about this on my blog a while back:
http://blog.dantup.com/2009/04/aspnet-mvc-handleerror-attribute-custom.html