Why is flutter printing out widget name? - flutter

I have a problem with flutter printing out the name and rendering Widget name after running the application
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
#override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
autoLogin() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
bool? loggedIn = prefs.getBool('loggedin');
if (loggedIn == true) {
Home();
} else {
return LoginOrSignup();
}
}
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(body:SafeArea(
child: FutureBuilder(
future: autoLogin(),
builder: (BuildContext context, snapshot) {
if (snapshot.hasData) {
return Text('${snapshot.data}');
} else {
return LoginOrSignup();
}
}),
))
);
}
}
After running the app the output is LoginOrSignup()
class LoginOrSignup extends StatelessWidget {
const LoginOrSignup({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Center(
child: MaterialButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => Login()),
);
},
child: Text('Loginsss'),
),
),
Center(
child: MaterialButton(
onPressed: (){
Navigator.push(
context,
MaterialPageRoute(builder: (context) => Signup()),
);
},
child: Text('Signup'),
),
)
],
),
);
}
}
I have tried using another widget like Text() but it still prints out the same when i run the application on a mobile app. The problem seems to appear in the autoLogin() function that i have

The issue is your future return Widget itself, and when you use Text('${snapshot.data}') it print the widget, To simplfity this you can return data from Future(this is what mostly we do). Let say you like to return widget itself.
A little correction is needed on Future.
Future<Widget> autoLogin() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
bool? loggedIn = prefs.getBool('loggedin');
if (loggedIn == true) {
return Home();
} else {
return LoginOrSignup();
}
}
And
return MaterialApp(
home: Scaffold(
body: SafeArea(
child: FutureBuilder<Widget>(
future: autoLogin(),
builder: (BuildContext context, snapshot) {
if (snapshot.hasData) {
return snapshot.data!;
} else {
return LoginOrSignup();
}
}),
)));

You are returning a Widget in autoLogin function. Instead you should return a bool.
Future<bool?> autoLogin() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
bool? loggedIn = prefs.getBool('loggedin');
if (loggedIn == null) return null;
if (loggedIn == true) {
return true;
} else {
return false;
}
}
Then in the FutueBuilder you can check if it's then return Home()
if (snapshot.hasData && snapshot.data! == true) {
return Home();
} else {
return LoginOrSignup();

Related

CheckboxListTitle trouble

I have a problem with my code. I want to control with a checkboxListTitle a list from an http request, as you know in the value: only true or false values are supported, however what I have is a string data type (0 false and 1 True). How could I fix this?, the closest I have been is with this code, but this way it only allows me to check or uncheck, not both at the same time and I can not see the real status of each data.
How could I treat each string in the list as a boolean so as not to lose this logic?
`Future<List> getData() async {
final response = await http.post(Uri.parse(baseurl + 'gettipo.php'),
body: {"categoria": widget.tipo});
return json.decode(response.body);
}
Widget build(BuildContext context) {
return RefreshIndicator(
onRefresh: () async {
setState(() {
getData();
});
},
child: FutureBuilder<List>(
future: getData(),
builder: ((context, snapshot) {
if (snapshot.hasError) print(snapshot.error);
return snapshot.hasData
? ItemList(
list: snapshot.data!,
)
: Center(
child: new CircularProgressIndicator(),
);
}),
),
);
}
}
class ItemList extends StatefulWidget {
final List list;
ItemList({required this.list});
#override
State<ItemList> createState() => _ItemListState();
}
class _ItemListState extends State<ItemList> {
#override
Widget build(BuildContext context) {
value: widget.list[index]['ocultar'] != 0,
onChanged: (val) {
setState(() {
widget.list[index]['ocultar'] = 0;
});`

How to return a widget if I need to go to a new screen?

Why do this code in not properly set up? I get the error: This function has a return type of 'Widget', but doesn't end with a return statement.
Obviously, it doesn like the use of Navigator stuff in future builder. How to make it properly?
MaterialApp(
home: const Splash1(),
);
class Splash1 extends StatelessWidget {
const Splash1({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
body: FutureBuilder<bool>(
future: checkIsSeen(),
builder: (BuildContext context, AsyncSnapshot<bool> snapshot) {
if (snapshot.hasData) {
if (snapshot.data == true) {
Navigator.pushReplacement(context,
MaterialPageRoute(builder: (context) => const HomeView()),
);
} else {
Navigator.pushReplacement(context,
MaterialPageRoute(builder: (context) => const IntroScreen()),
);
}
} else if (snapshot.hasError) {
return const Icon(
Icons.error_outline,
size: 60,
);
} else {
return CircularProgressIndicator();
}
}),
);
}
There is a statement about your issue (Obviously, it does not like the use of Navigator stuff in the future builder.). Future.builder shouldn't include logic beyond building widgets (e.g. don't call Navigator.push).
Instead of FutureBuilder, you can just put the async call in build().
Widget build(BuildContext context) {
check().then((success) {
if (success) {
Navigator.pushReplacementNamed(context, '/home');
} else {
Navigator.pushReplacementNamed(context, '/login');
}
});
You can learn more about this issue at this link: https://github.com/flutter/flutter/issues/16218
Since HomeView and IntroScreen both contain a Scaffold, you can reorganise your code like this, without using Navigator in the build method:
class Splash1 extends StatelessWidget {
const Splash1({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
return FutureBuilder<bool>(
future: checkIsSeen(),
builder: (BuildContext context, AsyncSnapshot<bool> snapshot) {
if (snapshot.hasData) {
return snapshot.data ? const HomeView() : const IntroScreen();
} else {
return Scaffold(
body: snapshot.hasError
? const Icon(
Icons.error_outline,
size: 60,
)
: const CircularProgressIndicator());
}
});
}
}

Use the futureBuilder for main route

I set up my app with named routes:
class App extends StatelessWidget {
const App({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
initialRoute: "/",
routes: {
"/": (context) =>
FutureBuilder(
future: landingPage(),
builder: (BuildContext context, AsyncSnapshot<Widget> widget) {
if(widget.connectionState == ConnectionState.done){
if(!widget.hasData) {
return const Center(
child: Text("No Data exists"),
);
}
return widget.data!;
}
return const Center(
child: CircularProgressIndicator(
color: Colors.white,
),
);
},
),
"/email-verification":(context) => const EmailVerificationPage(),
"/login":(context) => const LoginPage(),
"/name-age":(context) => const AgePage(),
"/signup":(context) => const SignupPage(),
},
);
}
}
Then I have my functions where I decide which page should be the landing page depending on how far the user has added information to their account:
Future<Widget> landingPage() async {
if(FirebaseAuth.instance.currentUser == null){
return const LoginPage();
}
if(!FirebaseAuth.instance.currentUser!.emailVerified){
return const EmailVerificationPage();
}
if(! await checkAge(FirebaseAuth.instance.currentUser!.uid)){
return const AgePage();
}
return const HomePage();
}
Future<bool> checkAge(String uid) async {
QuerySnapshot snapshot = await FirebaseFirestore.instance.collection("users")
.where("uid", isEqualTo: FirebaseAuth.instance.currentUser!.uid)
.where("birtDate", isNull: false)
.get();
if(snapshot.size > 0){
return true;
}
return false;
}
However the futureBuilder always returns no data for some reason. Does anyone know why?

How to pass Future<Either<Response, List<dynamic>>> function as parameter to Widget?

I created a general ListPage as below:
class ListPage extends StatelessWidget {
final context;
final Future<Either<Response, List<dynamic>>> futureFuncion;
final classListItem, classDetailPage;
const ListPage(this.context,this.futureFuncion,this.classListItem,this.classDetailPage, {Key key,}) : super(key: key);
#override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.all(10.0),
child: Column(children: [Expanded(child: _getData())]));
}
FutureBuilder _getData() {
return FutureBuilder<Either<ResponseError, List>>(
future: futureFuncion,
builder: (BuildContext context, AsyncSnapshot<Either<ResponseError, List>> snapshot) {
if (snapshot.hasError) {
return globals.showSnapshotError(snapshot);
} else if (!snapshot.hasData) {
return globals.showLoading('Loading...');
} else {
return snapshot.data.fold((l) {
return Error().showError(l.error, l.errorDescription);
}, (r) {
return ListView(children: _buildList(context, r));
});
}
},
);
}
}
I use code as below to create List Page:
class RequestListPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
String url = globals.debug ? 'assets/data/list.json' : 'domain.com/list';
return Scaffold(
appBar: AppBar(toolbarHeight: 50,centerTitle: true,title: Text("List")),
body: ListPage(
context,
debug
? LoadDataFromJsonFile().loadListData(context, url, '$Response')
: RequestApi().getListData(url, '$Response'),
RequestListItem,
RequestDetailPage,
),
);
}
Widget _buildList(context, list) {
List<Widget> children = [];
list.forEach((item) {
children.add(_buildTile(context, item));
});
return ListView(children: children);
}
ListTile _buildTile(context, item) {
return ListTile(
title: _buildListItem(context, item),
onTap: () => showDetail(context, item),
);
}
Widget _buildListItem(context, item) {
return classListItem;
}
void showDetail(context, item) {
Navigator.of(context).push(
MaterialPageRoute(builder: (BuildContext context) => classDetailPage(context, item)),
);
}
}
}
When I run this code, I got error as below, what can I do?
Attempted to use type 'RequestListItem' as a function. Since types do not define a method 'call', this is not possible. Did you intend to call the RequestListItem constructor and forget the 'new' operator?
Receiver: RequestListItem
Tried calling: RequestListItem(Instance of 'StatefulElement', Instance of 'Response')

navigation transition hangs during Future

i have the below code , that when the button is pressed on Page1() , the future is executed when Page2() loads, but the CircularProgressIndicator() "freezes", until the future completes. I have tried this with BottomNavigationBar as well, and the "slide-in" freezes half way there as well.
is there a more idiomatic way to do this so that Page2() renders fully while the future is running ?
//-----------------------
//main.dart
//-----------------------
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return new MaterialApp(
routes: {
'/' : (BuildContext context) => Page1(),
'/page2' : (BuildContext context) => Page2()
}
);
}
}
//-----------------------
//page1.dart
//-----------------------
class Page1 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Page 1')),
body: Container(
child: Column(children: <Widget>[
Text('Page 1 header'),
RaisedButton(
child: Text('Click me'),
onPressed: () {
Navigator.of(context).pushReplacementNamed('/page2');
})
],),)
);
}
}
//-----------------------
//page2.dart
//-----------------------
class Page2 extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return _Page2State();
}
}
class _Page2State extends State<Page2> {
MainModel model = MainModel();
void initState() {
model.fetchData();
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Page 2')),
body: ScopedModel<MainModel>(
model: model,
child: ScopedModelDescendant(
builder: (context, child, MainModel model) {
if (model.isLoading) {
return Center(child: CircularProgressIndicator());
} else {
return Container(
child: Column(
children: <Widget>[
Text('Page 2 body'),
],
));
}
})));
}
}
//-----------------------
//main_model.dart
//-----------------------
class MainModel extends Model {
bool _isLoading = false;
bool get isLoading => _isLoading;
void fetchData() async {
_isLoading = true;
notifyListeners();
final String url = 'https://jsonplaceholder.typicode.com/todos/1';
await http.get(url)
.then<Null>((http.Response response) {
print('${DateTime.now()} In http response and about to sleep');
sleep(const Duration(seconds:5));
print('${DateTime.now()} done sleeping');
_isLoading = false;
notifyListeners();
return;
}
).catchError((error) {
print('Error: $error');
_isLoading = false;
notifyListeners();
return;
});
}
}
The problem is that the sleep() method freezes the ui.
You could try a Future.delayed() in the following way:
class MainModel extends Model {
bool _isLoading = false;
bool get isLoading => _isLoading;
void fetchData() {
_isLoading = true;
notifyListeners();
final String url = 'https://jsonplaceholder.typicode.com/todos/1';
http.get(url).then<Null>((http.Response response) {
print('${DateTime.now()} In http response and about to sleep');
Future.delayed(Duration(seconds: 5), () {
_isLoading = false;
notifyListeners();
print('${DateTime.now()} done sleeping');
});
}).catchError((error) {
print('Error: $error');
_isLoading = false;
notifyListeners();
return;
});
}
}
Flutter recommends using the FutureBuilder widget when working with async data sources. For example:
FutureBuilder<Post>(
future: fetchPost(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return Text(snapshot.data.title);
} else if (snapshot.hasError) {
return Text("${snapshot.error}");
}
// By default, show a loading spinner
return CircularProgressIndicator();
},
);
This way the CircularProgressIndicator will keep running whilst your Page2 loads the API data.