I have icon-name in my database called (for example) "favorite", "people, "home", etc. The code :
Icon(Icons.favorite, size: 25)
How can I replace that to string? I have tried flutter_icons, material_design_icons_flutter, etc.
Icon(Icons.icon.name, size: 25) //icon.name is the method to call the string from API database
Hope you get what I was trying to explain.
You can copy paste run full code below
You can use getIconUsingPrefix of package https://pub.dev/packages/icons_helper and FutureBuilder
Demo code simulate retrieve DB dealy
Future<String> iconName() async {
await Future.delayed(Duration(seconds: 5), () {});
return Future.value("favorite");
}
...
FutureBuilder(
future: _future,
builder: (context, AsyncSnapshot<String> snapshot) {
switch (snapshot.connectionState) {
...
case ConnectionState.done:
if (snapshot.hasError) {
...
} else {
return Icon(getIconUsingPrefix(name: snapshot.data),
size: 25.0);
}
}
})
working demo
full code
import 'package:flutter/material.dart';
import 'package:icons_helper/icons_helper.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo + Icon Helper Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
Future<String> _future;
Future<String> iconName() async {
await Future.delayed(Duration(seconds: 5), () {});
return Future.value("favorite");
}
#override
void initState() {
_future = iconName();
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
FutureBuilder(
future: _future,
builder: (context, AsyncSnapshot<String> snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
return Text('none');
case ConnectionState.waiting:
return Center(child: CircularProgressIndicator());
case ConnectionState.active:
return Text('');
case ConnectionState.done:
if (snapshot.hasError) {
return Text(
'${snapshot.error}',
style: TextStyle(color: Colors.red),
);
} else {
return Icon(getIconUsingPrefix(name: snapshot.data),
size: 25.0);
}
}
}),
],
),
),
);
}
}
you need to create a map with 'iconName' as key, and IconData as value. and after fetching iconName from db use it to get IconData;
Related
I'd like a FutureBuilder to wait for the user to make a selection in a route or dialog (example below) and then return that data to the builder. However, the dialog never appears.
How can I await for data to be returned from a screen or dialog using a FutureBuilder?
DartPad
import 'package:flutter/material.dart';
void main() => runApp(HomeScreen());
class HomeScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: FutureBuilder(
future: launch(context),
builder: (context, snapshot) {
// use result from screen or dialog in snapshot.data
return Center(child: Text(snapshot.data));
}),
),
);
}
Future launch(BuildContext context) async {
return await showDialog<void>(
context: context,
builder: (BuildContext context) {
return AlertDialog(
actions: <Widget>[
FlatButton(
child: Text('Send Data'),
onPressed: () {
// return some data
Navigator.pop(context, 'Some data!');
},
),
],
content: Container(child: Text('')),
);
},
);
}
}
You can copy paste run full code below
Step 1: You need to use addPostFrameCallback
Step 2: move MaterialApp to upper level
Step 3: check ConnectionState
code snippet
#override
void initState() {
WidgetsBinding.instance.addPostFrameCallback((_) {
setState(() {
_future = launch(context);
});
});
super.initState();
}
Future<String> launch(BuildContext context) async {
var result = await showDialog(
...
print("result $result");
return Future.value(result);
}
working demo dartpad link
full code
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: HomeScreen(),
);
}
}
class HomeScreen extends StatefulWidget {
#override
_HomeScreenState createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
Future<String> _future;
#override
void initState() {
WidgetsBinding.instance.addPostFrameCallback((_) {
setState(() {
_future = launch(context);
});
});
super.initState();
}
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
body: FutureBuilder(
future: _future,
builder: (context, AsyncSnapshot<String> snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
return Text('none');
case ConnectionState.waiting:
return Center(child: CircularProgressIndicator());
case ConnectionState.active:
return Text('');
case ConnectionState.done:
if (snapshot.hasError) {
return Text(
'${snapshot.error}',
style: TextStyle(color: Colors.red),
);
} else {
return Center(child: Text("his this is ${snapshot.data}"));
}
}
}),
),
);
}
Future<String> launch(BuildContext context) async {
var result = await showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
actions: <Widget>[
FlatButton(
child: Text('Send Data'),
onPressed: () {
// return some data
Navigator.pop(context, 'Some data!');
},
),
],
content: Container(child: Text('')),
);
},
);
print("result $result");
return Future.value(result);
}
}
Thanks for reverting use the future Completer to get the data from Dialogue
create a Completer instance
var dialogueFuture = Completer();
feed the future of completer to Future builder
FutureBuilder{
future : dialogueFuture.future,
...
}
in show dialogue function complete the Future like this
var theData = await showDialogue(...)
dialogFuture.complete(theData);
see the dartpad here
I'm trying to make it that the user must be logged in when veiwing specific pages. If not it redirects them to the login page.
I have the following code, but when I add Navigator.pushReplacementNamed(context, '/login'); I get an error:
The following assertion was thrown building
FutureBuilder<Response>(dirty, state:
_FutureBuilderState<Response>#458f7):
return FutureBuilder<Response>(
future: Provider.of<LocationProvider>(context).fetchAllLocations(),
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
return Text('Fetch Items');
break;
case ConnectionState.active:
return Text('is active');
break;
case ConnectionState.waiting:
return Text('Is Fetching');
break;
case ConnectionState.done:
if (snapshot.data.status == Status.ERROR)
return Text('errrrrrroooorr');
else if (snapshot.data.status == Status.UNAUTHENTICATED) {
Navigator.pushReplacementNamed(context, '/login');
return Container();
}
return MyWidget();
break;
default:
return Text('hy');
}
},
);
Also, how can I make this code reusable?
You can use addPostFrameCallback
code snippet
WidgetsBinding.instance.addPostFrameCallback((_) {
Navigator.pushReplacementNamed(context, '/login');
});
working demo
full test code
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
initialRoute: '/',
routes: {
'/': (context) => MyHomePage(
title: "demo",
),
'/login': (context) => Login(),
},
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
Future<String> _future;
Future<String> fetchAllLocations() async {
await Future.delayed(Duration(seconds: 3), () {});
Future.value("test");
}
#override
void initState() {
_future = fetchAllLocations();
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: FutureBuilder(
future: _future,
builder: (context, AsyncSnapshot<String> snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
return Text('none');
case ConnectionState.waiting:
return Center(child: CircularProgressIndicator());
case ConnectionState.active:
return Text('');
case ConnectionState.done:
if (snapshot.hasError) {
return Text(
'${snapshot.error}',
style: TextStyle(color: Colors.red),
);
} else {
WidgetsBinding.instance.addPostFrameCallback((_) {
Navigator.pushReplacementNamed(context, '/login');
});
return Container();
}
}
}));
}
}
class Login extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Second Screen"),
),
body: Center(
child: RaisedButton(
onPressed: () {
// Navigate back to first screen when tapped.
},
child: Text('Go back!'),
),
),
);
}
}
I am trying to reproduce the hacker news app as shown in this video : https://youtu.be/fahC3ky_zW0. However in my StreamBuilder Snapshot connection state is always coming out to be 'waiting'. Hence my app is not able to populate data.
File: hn_bloc.dart
class HackerNewsBloc {
final _articlesSubject = BehaviorSubject<UnmodifiableListView<Article>>();
var _articles = <Article>[];
List<int> _ids = [
23361987,
23366546,
];
HackerNewBlock() {
_updateArticles().then((_) {
_articlesSubject.add(UnmodifiableListView(_articles));
});
}
Stream<UnmodifiableListView<Article>> get articles => _articlesSubject.stream;
Future<Article> _getArticle(int id) async {
final storyUrl = 'https://hacker-news.firebaseio.com/v0/item/$id.json';
final storyRes = await http.get(storyUrl);
return parseArticle(storyRes.body);
}
Future<Null> _updateArticles() async {
final futureArticles = _ids.map((id) => _getArticle(id));
final articles = await Future.wait(futureArticles);
_articles = articles;
return null;
}
}
File main.dart
import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:hackernews/src/article.dart';
import 'package:hackernews/src/hn_bloc.dart';
import 'dart:collection';
import 'dart:async';
void main() {
final hnBloc = HackerNewsBloc();
runApp(MyApp(
bloc: hnBloc,
));
}
class MyApp extends StatelessWidget {
final HackerNewsBloc bloc;
MyApp({
Key key,
this.bloc,
}) : super(key: key);
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Hacker News',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(
title: 'Hacker News',
bloc: bloc,
),
);
}
}
class MyHomePage extends StatefulWidget {
final String title;
final HackerNewsBloc bloc;
MyHomePage({Key key, this.title, this.bloc}) : super(key: key);
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: StreamBuilder<UnmodifiableListView<Article>>(
stream: widget.bloc.articles,
initialData: UnmodifiableListView<Article>([]),
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
return Text('Press button to start.');
case ConnectionState.active:
case ConnectionState.waiting:
return Text('Awaiting result...');
case ConnectionState.done:
if (snapshot.hasError) return Text('Error: ${snapshot.error}');
return ListView(
children: snapshot.data.map(_buildItem).toList(),
);
// You can reach your snapshot.data['url'] in here
}
return null; // unreachable
},
),
);
}
Widget _buildItem(Article article) {
print(article);
return Padding(
key: Key(article.title),
padding: const EdgeInsets.all(16.0),
child: ExpansionTile(
title: Text(
article.title,
style: TextStyle(fontSize: 24),
),
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Text("${article.type}"),
IconButton(
icon: Icon(Icons.launch),
onPressed: () async {
if (await canLaunch(article.url)) {
launch(article.url);
}
},
),
],
),
],
),
);
}
}
i want to have switch widget in settings page. i can figured switching theme using switch widget, but this is too complicated
my main_page:
import 'package:intl/intl.dart';
Column(
children: <Widget>[
Align(
alignment: Alignment.topCenter,
child: Text("Summary"),
),
SizedBox(height: 45),
_time24(snapshot), //show this if switch is off
_time12(snapshot), //show this if switch is on
],
)
Widget _time12(AsyncSnapshot snapshot){
return Column(
children: <Widget>[
_time(Icons.home, Text(DateFormat("hh:mma").format(DateFormat("HH:mm").parse(snapshot.data.home)))),
_time(Icons.work, Text(DateFormat("hh:mma").format(DateFormat("HH:mm").parse(snapshot.data.work)))),
_time(Icons.restaurant, Text(DateFormat("hh:mma").format(DateFormat("HH:mm").parse(snapshot.data.restaurant)))),
]);
}
Widget _time24(AsyncSnapshot snapshot){
return Column(
children: <Widget>[
_time(Icons.home, Text(snapshot.data.home)),
_time(Icons.work, Text(snapshot.data.work)),
_time(Icons.restaurant, Text(snapshot.data.restaurant)),
]);
}
Thank you for your time :)
You can copy paste run full code below
You can await Navigator.push then call setState()
code snippet
onPressed: () async {
await Navigator.push(
context,
MaterialPageRoute(builder: (context) => Setting()),
);
setState(() {});
}
...
else if (snapshot.hasData) {
if (is24) {
return _time24(snapshot);
} else {
return _time12(snapshot);
}
}
working demo
full code
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class Payload {
String home;
String work;
String restaurant;
Payload({this.home, this.work, this.restaurant});
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
Future _future;
void _incrementCounter() {
setState(() {
_counter++;
});
}
Widget _time12(AsyncSnapshot snapshot) {
var a = DateFormat("HH:mm").parse(snapshot.data.home);
print("a ${a}");
return Column(children: <Widget>[
_time(
Icons.home,
Text(DateFormat("hh:mma")
.format(DateFormat("HH:mm").parse(snapshot.data.home)))),
_time(
Icons.work,
Text(DateFormat("hh:mma")
.format(DateFormat("HH:mm").parse(snapshot.data.work)))),
_time(
Icons.restaurant,
Text(DateFormat("hh:mma")
.format(DateFormat("HH:mm").parse(snapshot.data.restaurant)))),
]);
}
Widget _time24(AsyncSnapshot snapshot) {
return Column(children: <Widget>[
_time(Icons.home, Text(snapshot.data.home)),
_time(Icons.work, Text(snapshot.data.work)),
_time(Icons.restaurant, Text(snapshot.data.restaurant)),
]);
}
Widget _time(IconData iconData, Text _text) {
return ListTile(
leading: Icon(iconData),
title: _text,
);
}
Future<Payload> getData() {
print("getData");
return Future.value(
Payload(home: "18:00", restaurant: "13:00", work: "08:00"));
}
#override
void initState() {
// TODO: implement initState
_future = getData();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
FutureBuilder<Payload>(
future: _future,
builder: (BuildContext context, AsyncSnapshot<Payload> snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
return Text('Press button to start.');
case ConnectionState.active:
case ConnectionState.waiting:
return Text('Awaiting result...');
case ConnectionState.done:
if (snapshot.hasError)
return Text('Error: ${snapshot.error}');
else if (snapshot.hasData) {
if (is24) {
return _time24(snapshot);
} else {
return _time12(snapshot);
}
}
}
return null; // unreachable
},
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () async {
await Navigator.push(
context,
MaterialPageRoute(builder: (context) => Setting()),
);
setState(() {});
},
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
bool is24 = true;
class Setting extends StatefulWidget {
#override
_SettingState createState() => _SettingState();
}
class _SettingState extends State<Setting> {
void _changed(value) {
setState(() {
is24 = value;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Second Route"),
),
body: Container(
child: Switch(
value: is24,
onChanged: _changed,
)));
}
}
I have created a Flutter application with a list. On tap of an item, I am opening detail of that item.
The problem is whenever I come back from the detail screen, the list screen is reloaded. I don't want to reload the list every time.
I have used BloC architecture in this.
Below are the code snippets. Please suggest.
Thank You.
Main
void main() {
final userRepository = UserRepository();
ApiClient apiClient = ApiClient(httpClient: http.Client());
runApp(BlocProvider<AuthenticationBloc>(
builder: (context) {
return AuthenticationBloc(
userRepository: userRepository, apiClient: apiClient)
..dispatch(AppStarted());
},
child: MyApp(userRepository: userRepository),
));
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
final UserRepository userRepository;
MyApp({Key key, #required this.userRepository}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: BlocBuilder<AuthenticationBloc, AuthenticationState>(
bloc: BlocProvider.of<AuthenticationBloc>(context),
builder: (context, state) {
if (state is AuthenticationUninitialized) {
return SplashPage();
}
if (state is AuthenticationAuthenticated) {
return HomePage(userRepository: userRepository);
}
if (state is AuthenticationUnauthenticated) {
return LoginPage(userRepository: userRepository);
}
if (state is AuthenticationLoading) {
return LoadingIndicator();
}
return null;
},
),
);
}
}
List Screen
class HomePage extends StatelessWidget {
UserRepository userRepository;
HomePage({#required this.userRepository}) : super();
#override
Widget build(BuildContext context) {
ApiClient apiClient = ApiClient(httpClient: http.Client());
return Scaffold(
appBar: AppBar(
title: Text('Home'),
),
drawer: AppDrawer(userRepository),
body: BlocProvider(
builder: (context) {
return HomeBloc(apiClient);
},
child: _HomeContent(),
),
floatingActionButton: FloatingActionButton(
onPressed: () {},
child: Icon(Icons.add),
backgroundColor: Colors.amberAccent,
),
);
}
}
class _HomeContent extends StatelessWidget {
#override
Widget build(BuildContext context) {
final HomeBloc homeBloc = BlocProvider.of<HomeBloc>(context);
homeBloc.dispatch(FetchMovieList());
return BlocBuilder<HomeBloc, HomeState>(
builder: (context, state) {
if (state is MovieListLoading) {
return Center(
child: CircularProgressIndicator(),
);
}
if (state is MovieListLoaded) {
List<Movie> topRatedMovies = state.movieList;
return new ListView.builder(
itemBuilder: (BuildContext context, int index) {
return new ListTile(
title: Card(
child: Row(
children: <Widget>[
Image.network(ApiClient.IMAGE_BASE_URL +
topRatedMovies[index].poster_path),
Column(
children: <Widget>[
Text(topRatedMovies[index].title),
],
)
],
),
),
onTap: () {
_onListItemTapped(topRatedMovies[index].id, context);
},
);
},
itemCount: topRatedMovies.length,
);
}
if (state is MovieListError) {
return Center(
child: Text('Error in calling API'),
);
}
return Center(child: Text('Employee data not found'));
},
);
}
void _onListItemTapped(int movieId, BuildContext context) {
Navigator.push(context,
MaterialPageRoute(
builder: (context) => MovieDetailPage(
movieId: movieId,
)));
}
}
At anytime your build method needs to be ready for multiple build calls. If build calls are causing problem then something is probably wrong. It would be a better idea to fetch the data outside the build method to prevent unnecessary API calls.
For example you can create a Stateful Widget and in initState method you can fetch the data. After that, build method is called to prepare UI with the data. You can use a Future Builder to show progress and update UI when the data is fetched.
Example:
class MyWidget extends StatefulWidget {
#override
_MyWidgetState createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
Future _future;
Future getData() async {
// Fetch data
}
#override
void initState() {
super.initState();
_future = getData();
}
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: _future,
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
case ConnectionState.waiting:
return Center(child: CircularProgressIndicator());
default:
if (snapshot.data.hasErrors) {
return Text('Error: ${snapshot.data.errors}');
} else {
// Data is fetched, build UI
return ListTile();
}
}
});
}
}