My process is as follows. The screen has just two elements - TextFormField and an ElevatedButton.
This is my first Flutter/Dart program FYI, so I might be making a beginner mistake.
Question: The very first line of the FutureBuilder isn't executed. No error, no messages, nothing. Why does this happen?
The user enters the email address, clicks the button, the fetchClientInfo
function is executed, which returns a Future<ClientInfo>
and that's that.
Could you help please?
@override
Widget build(BuildContext context) {
final _formKey = GlobalKey<FormState>();
return Scaffold(
appBar: AppBar(
title: Text("Register Profile"),
),
body: Form(
key: _formKey,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Padding(
padding: EdgeInsets.all(20),
child: TextFormField(
controller: emailController,
validator: (email) {
if (email.isEmpty) {
return 'Please enter your email address.';
} else if (!EmailValidator.validate(email)) {
return 'Please enter a valid email address.';
}
return null;
},
decoration: InputDecoration(
border: new UnderlineInputBorder(borderSide: new BorderSide(color: Colors.red)),
labelText: 'Email',
hintText: 'Enter your email address',
contentPadding: EdgeInsets.all(20.0),
),
)),
ElevatedButton(
onPressed: () => {
if (_formKey.currentState.validate())
{
FutureBuilder<ClientInfo>(
future: fetchClientInfo(emailController.text),
builder: (BuildContext context, snapshot) {
print("here");
if (snapshot.data.outcome) {
return Text("main screen");
} else if (!snapshot.data.outcome) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
RegisterNewUser(emailAddress: emailController.text)));
} else if (snapshot.hasError) {
return Text("${snapshot.error}");
}
// Show a spinner
return CircularProgressIndicator();
},
)
}
},
child: Text(
"Check Email",
))
])));
}
Future<ClientInfo> fetchClientInfo(String emailAddress) async {
var url = Uri.https(APIAccess.baseAPIURL, APIAccess.pathToClientAPI, {
'client_id': '$emailAddress',
'action': 'info',
'key': '${APIAccess.key}'
});
final response = await http.get(url);
if (response.statusCode == 200) {
return ClientInfo.fromJson(jsonDecode(response.body));
} else {
throw Exception('Failed to load album');
}
}
You're missing a couple of things:
FutureBuilder
but it's not being used anywhere)if(true) {
return SizedBox.shrink();
}
// that's how compiler sees it
Map<dynamic, dynamic> Function() foo = () => {
};
What you did reminds me of javascript, but in dart lambdas look a bit different
return Button(
onTap: () => doStuff(),
);
return Button(
onTap: () {
doStuff();
}
);
// and if you want to return a value from block lambda
return Builder(
builder: (context) {
return SizedBox.shrink();
}
);
What you want to read about is state management. The topic is highly opinionated, so you have to choose yourself the solution that's right for you. https://flutter.dev/docs/development/data-and-backend/state-mgmt
I myself like using a slightly modified version of bloc. You can find the 'original' one here: https://pub.dev/packages/flutter_bloc
A new de-facto standard if it comes to state management is Riverpod
If you just want to make your code work, do something like this:
class Demo extends StatefulWidget {
@override
_DemoState createState() => _DemoState();
}
class _DemoState extends State<Demo> {
Future<ClientInfo?> clientInfo = Future.value(null);
@override
Widget build(BuildContext context) {
return Column(
children: [
ElevatedButton(
onPressed: () {
setState(() {
clientInfo = fetchClientInfo(emailController.text);
});
},
child: _buildButtonContent(),
),
FutureBuilder<ClientInfo>(
initialData: null,
future: clientInfo,
builder: (BuildContext context, snapshot) {
if (snapshot.data == null) {
return SizedBox.shrink();
} else {
return Text(snapshot.data.toString());
}
},
)
],
);
}
}