I am a flutter beginner and trying to write an app.
For 'TextFormField' widget, we can get its value using the 'TextEditingController' like below,
TextEditingController titleController = TextEditingController();
.
.
.
expTitle = titleController.text;
Is there a simillar way to get the value from 'DateTimePickerFormField' widget using controller ? Currently I pick the value via 'onChanged' property of DateTimePickerFormField like below
onChanged: (dt) => setState(() => datePicked = dt)
you can use TextEditingController for DateTimePickerFormField
Related
Hello I'm developing an App but it stopped because I have a problem that I'm going to explain it:
I made a Login screen to my app located in main.dart file. In this file I do a query to my server using a PHP file using JSON to encode the query and in the dart file i use the Json.decode to validate the data.
Now the question is how can I put that data in a variable to use it in another Widget? What I'm trying to do is putting the name and the email from the query to my side-bar menu which is my HomePage Widget.
Dart:
class LoginStates extends State<Login>{
#override
Widget build(BuildContext context) {
TextEditingController user=new TextEditingController();
TextEditingController pass=new TextEditingController();
Future<List> _login() async{
final response =await
http.post("https://exclinico.com/admin/FlutterClases/login.php", body: {
"correo": user.text,
"password": pass.text,
});
print(response.body);
var datosusuario = json.decode(response.body);
if (datosusuario.length==0) {
Mensaje(context);
}
else{
Navigator.pushReplacementNamed(context, '/mainpage');
}
}
PHP:
<?php
include("conexion.php");
$usuario=$_POST['correo'];
$contraseña=$_POST['password'];
$contraseña=base64_encode($contraseña);
$query=$conexion->query("SELECT*FROM paciente WHERE correo_elect = '$usuario' AND password='$contraseña'");
$resultado=array();
while($fetchData=$query->fetch_assoc()){
$resultado[]=$fetchData;
}
$_SESSION['correo'] = $usuario;
echo json_encode($resultado);
?>
I need to transfer the name and email to HomePage. Sorry for the English BTW! Thanks.
There is a StreamBuilder (using RxDart) which displays some date. After click on InkWell widget I need to calculate a new date on basis of old one. The code below simply explains the algo but when I run it there is nothing happens and execution stops after underlined row, i.e. I never see the value of lastCalcDate.
GUI:
child: StreamBuilder(
stream: bloc.getDate,
builder: (context,snapshot) {
return InkWell(
onTap: () => tapHandler
);
}),
void tapHandler() async {
var lastCalcDate = await bloc.getDate.single;
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
print(lastCalcDate);
var newCalcDate = lastCalcDate.add(Duration(days:1));
bloc.setDate(newCalcDate)
}
BLoC:
class Bloc {
// BehaviourSubject is usedto be sure that last sent date will be accessible in `tapHandler`.
final _dateSubject = BehaviourSubject<DateTime>();
Observable<DateTime> get getDate => _dateSubject.stream;
Function(DateTime) get setDate => _dateSubject.add;
}
To implement what I need I created some workaround but I don't like it because I fill that I can do the same thing using observables.
BLoC (workaround):
class Bloc {
final _dateSubject = BehaviourSubject<DateTime>();
Observable<DateTime> get getDate => _dateSubject.stream;
DateTime _date;
void setDateWorkaround(DateTime date) {
_date = date;
_dateSubject.add(date);
}
}
Could you someone to give me advise. What I did wrong?
single will not work because it is going to return the next item in the stream, however, that has to be added first. This means that single will just wait for the next item and in your case it will not happen.
Since you are using rxdart and BehaviorSubject already, you can easily access the current element like this:
class Bloc {
final _dateSubject = BehaviourSubject<DateTime>();
Observable<DateTime> get getDate => _dateSubject.stream;
Function(DateTime) get setDate => _dateSubject.add;
DateTime get currentDate => _dateSubject.value;
}
In this case, I am making use of BehaviorSubject.value, which is actually the whole point of that class.
Now, you can just use currentDate in your tap handler:
void tapHandler() async {
var lastCalcDate = bloc.currentDate;
print(lastCalcDate);
var newCalcDate = lastCalcDate.add(Duration(days:1));
bloc.setDate(newCalcDate)
}
Use StreamProvider from provider
Listen to a Stream and expose the latest value emitted.
I'm trying to add a user's first name and last name to cells A and B to the target google sheet but I keep getting the error :
[ERROR:flutter/lib/ui/ui_dart_state.cc(148)] Unhandled Exception: type
'String' is not a subtype of type 'List' in type cast
whenever I submit my code.
I know that the error is isolated to the clientViaServiceAccount block of code because that's the line of code that the error stack points to. I've tried changing _scopes to [SheetsApi.SpreadsheetsScope] in the parameters but that led to the same thing.
I modeled this block of code based off of the example here:
https://pub.dartlang.org/packages/googleapis#-example-tab-
and the second answer here:
Google Sheets API v4 for Flutter/Dart
if (_formKey.currentState.validate()){
Navigator.push(
context,
MaterialPageRoute(builder: (context) => ConfirmationPage()),
);
_formKey.currentState.save();
final _privateKey = { 'private JSON key' };
final _cred = new ServiceAccountCredentials.fromJson(_privateKey);
final _scopes = [SheetsApi.SpreadsheetsScope];
Map<String,String> _sheets = {
"ISMI": 'sheet key',
"NORCO": 'sheet key 2'
};
clientViaServiceAccount(_cred, _scopes).then((client){
var api = new SheetsApi(client);
ValueRange vr = new ValueRange.fromJson({"values": [_newUser.firstName,_newUser.lastName]});
api.spreadsheets.values.append(vr,_sheets[_newUser.location],'A:B').then((AppendValuesResponse r) {
client.close();
});
});
}
Any help would be much appreciated,
Thank You
The problem was that the JSON needs to be surrounded by r''''''. So I changed the _privateKey variable to this:
final _privateKey = r'''{private JSON key}''';
I have encountered an issue while experimenting with flutter.
I have an AppBar with some Actions.
One of these actions is a calendar widget. My desired behavior will be by the new date selection the data on my Scaffold to be changed accordingly.
The issue is that, although I have managed to accomplished this behavior, the call to my API performed twice. I have identify that the issue was the RefreshIndicator that I had put in place (in order for the user to pull to refresh the page on demand), but I do not understand why...
For some reason when I change the date and consequently the data changed, it identify this as refresh state and then executes _handleRefresh(). The problem is, I still want to have the pull-down-to-refresh behavior.
Files on (tabView.dart file)
Scaffold's widget tree
RefreshIndicator(
key: _modelRefreshKey,
child: ListView.builder(
itemCount: this._fetchedData?.length,
itemBuilder: (BuildContext context, int index) {
if (this._fetchedData!= null) {
final MyModel myModel = this._fetchedData[index];
return (index == 0)
? ResultsLayout(
model: myModel ,
lastUpdateTxt: myModel.someTXT,
)
: MyModelInheritedWidget(
model: myModel,
child: ModelCardLayout(),
);
} else {
return EmptyWidget();
}
},
),
onRefresh: _handleRefresh,
),
Handle on refresh function
Future<Null> _handleRefresh() async {
Completer<Null> completer = new Completer<Null>();
this.getData().then((_) {
completer.complete();
});
return completer.future;
}
On select new date this function executes which refresh call again the data (hometab.dart file)
if (picked != null && picked != _selectedDate) {
_selectedDate = picked;
modelRefreshKey.currentState.widget.selectedDate = picked;
modelRefreshKey.currentState?.getData();
}
It is worth to point out the date method is located at where I create the tabs and the actual data to refresh is a part of a tab. I mention this in case it is some how related to my issue.
Any insights will be really helpful.
Thanks in advance.
I would have a variable like the following to see if the app is waiting for an API response:
_isWaitingForResponse = false;
Future getData() {
if(_isWaitingForResponse) return;
_isWaitingForResponse = true;
//change _isWaitingForResponse on api's response
}
I have used Fluro package inside my Flutter app, and I implemented all of my routes with a handler like the example of this package.
Could you please let me know what is the correct way to pass an object or list of objects between routes?
You could do that via ModalRoute.of(context).settings.arguments
so if you have object:
class ScreenArguments {
final String title;
ScreenArguments(this.title);
}
navigate with:
Navigator.of(context).pushNamed("/screen", arguments: ScreenArguments("Title"));
and in handler:
static Handler _screenHandler = Handler(handlerFunc: (BuildContext context, Map<String, dynamic> params) {
final ScreenArguments args = ModalRoute.of(context).settings.arguments;
return PassArgumentsScreen(args?.title);
});
The solution presented at How to pass JSON response to another view works unless your object has a string (or the decoded JSON contains) a / as #Chirag said.
I'm giving my solution as it worked, since my object has an URL inside.
Given the router:
_router.define("/groups/:group", handler: Handler(handlerFunc: (context, params) {
String param = params["group"][0];
GroupAPIModel group = GroupAPIModel.fromJson(decodeJsonDataForFluro(param));
return GroupDetailsScreen(group);
}));
Defining:
String encodeJsonDataForFluro(Map<String, dynamic> mapToEncode) {
return jsonEncode(mapToEncode).replaceAll("/", HtmlEscape().convert("/"));
}
Map<String, dynamic> decodeJsonDataForFluro(String encodedMap) {
return jsonDecode(encodedMap.replaceAll(HtmlEscape().convert("/"), "/"));
}
This method would reach that route:
void _onGroupClicked(GroupAPIModel group) {
String bodyJson = encodeJsonDataForFluro(group.toJson());
router.navigateTo(context: context, path: "/groups/$bodyJson");
}
Just struggled with this and found a solution. You can do this:
Object object = <Your Object>
Navigator.of(context).pushNamed("/page/" + object.id.toString(),
arguments: object);
And then access the custom object in Fluro like this:
static void setupRouter() {
router.define("page/:pageId", handler: _pageHandler);
}
static Handler _pageHandler =
Handler(handlerFunc: (BuildContext context, Map<String, dynamic> params) {
Object object = ModalRoute.of(context).settings.arguments;
return Widget(object);
});
While Fluro doesn't have this, this is a solution that isn't completely terrible.
I used to pass the data as an encoded JSON and String format like all other solutions in this question. Recently, the Fluro plugin provides a way of pass arguments as a class object between the routes like a Flutter navigator.
After pushing a route with a custom RouteSettings you can use the BuildContext.settings extension to extract the settings. Typically this would be done in Handler.handlerFunc so you can pass RouteSettings.arguments to your screen widgets.
/// Push a route with custom RouteSettings if you don't want to use path params
FluroRouter.appRouter.navigateTo(
context,
'home',
routeSettings: RouteSettings(
arguments: MyArgumentsDataClass('foo!'),
),
);
/// Extract the arguments using [BuildContext.settings.arguments] or [BuildContext.arguments] for short
var homeHandler = Handler(
handlerFunc: (context, params) {
final args = context.settings.arguments as MyArgumentsDataClass;
return HomeComponent(args);
},
);
In the case of using the Flutter navigator instead of the Fluro plugin use this link or check the following approach.
The Navigator provides the ability to navigate to a named route from any part of an app using a common identifier. In some cases, you might also need to pass arguments to a named route. For example, you might wish to navigate to the /user route and pass information about the user to that route.
To pass different pieces of data, create a class that stores this information.
// You can pass any object to the arguments parameter.
// In this example, create a class that contains a customizable
// title and message.
class ScreenArguments {
final String title;
final String message;
ScreenArguments(this.title, this.message);
}
Now you need to define a handler using ModalRoute:
static Handler _screenHandler = Handler(
handlerFunc: (BuildContext context, Map<String, dynamic> params) {
final ScreenArguments args = ModalRoute
.of(context)
.settings
.arguments;
return PassArgumentsScreen(args?.title);
});
And navigate with:
Navigator.pushNamed(
context,
"/screen",
arguments: ScreenArguments(
'Class Arguments Screen',
'This message is extracted in the build method.',
),
);