How can I display Image A on the user's screen if it is false or Image B if it is true, Image A is the first one that appears, when the user clicks on it, the state changes to true and switches to Image B, and switches once the user clicks on it, the state changes to true or false.
Image A = false
Image B = true
Image A - Image B
class _MyAppState extends State<MyApp> {
bool closedImage = false;
bool openImage = true;
bool switchOn = false;
void _onSwitchChanged(bool value) {
setState(() {
switchOn = false;
});
}
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData(scaffoldBackgroundColor: Colors.white),
home: Scaffold(
appBar: AppBar(
backgroundColor: Colors.white,
elevation: 0,
),
body:
Center(
child: InkWell(
onTap: () {
Switch(
onChanged: _onSwitchChanged,
value: switchOn,
);
},
child: Container(
color: Colors.white,
child: ClipRRect(
child: switchOn ? Image.asset('lib/assets/closed.png') : Image.asset('lib/assets/open.png')
)
),
),
)
),
);
}
}
Just toggle the switchOn variable like this:
void _onSwitchChanged(bool value) {
setState(() {
switchOn = !switchOn;
});
}
I think your method _onSwitchChanged needs to use the incoming bool value argument (which is supplied by the Switch).
Here's a similar example showing typical usage:
import 'package:flutter/material.dart';
class SwitchFieldPage extends StatefulWidget {
#override
_SwitchFieldPageState createState() => _SwitchFieldPageState();
}
class _SwitchFieldPageState extends State<SwitchFieldPage> {
bool switchVal = false;
String monkey = 'A';
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Switch Field'),
),
body: SafeArea(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Text('Monkey $monkey'),
Switch(
onChanged: (val) { // ← remember to use val (bool)
print('Switch value: $val');
setState(() {
switchVal = val; // this sets the Switch setting on/off
monkey = val ? 'B' : 'A'; // change your monkey source
});
},
value: switchVal,
)
],
),
),
),
);
}
}
You can use a GestureDetector or InkWell to detect when the user presses on the image. For updating the image, I'd suggest learning state management. To make this simple for now, we're going to use StreamBuilder.
screen.dart:
final ScreenBloc _screenBloc = ScreenBloc();
// This is inside your widget build
StreamBuilder<AuthState>(
stream: _screenBloc.pic,
initialData: false,
builder: (context, snapshot) {
return GestureDetector(
onTap: ()=> _screenBloc.toggle(),
child: snapshot.data?Image.asset('lib/assets/closed.png') : Image.asset('lib/assets/open.png'),
);
},
)
screen_bloc.dart:
class ScreenBloc{
bool _currentState=false;
StreamController<bool> _picStream = StreamController<bool>();
Stream<bool> get pic => _picStream.stream;
void toggle(){
_currentState=!_currentState;
_picStream.add(_currentState);
}
}
Related
I have a Switch on a screen and I need it to use the value that is in a Provider. I've tried to infer this value using the provider's value, but the Switch is immobile, it doesn't change visually(but the value is changed in the DB), it only works as it should when I remove the provider's inferences.
My Provider: (It is being called when I start the application)
class DailyDatabase with ChangeNotifier {
bool notificationActive = false;
void loadDailyData() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
notificationActive = prefs.getBool('notificationActive') ?? false;}
Variable:
#override
Widget build(BuildContext context) {
final provider = Provider.of<DailyDatabase>(context);
_notificationActive = provider.notificationActive;
Switch:
Switch(
value: _notificationActive,
onChanged: (value) {
_notificationActive = value;
provider.setNotification(value);
},
),
Stateful Version - Provider only
Here's a very basic example of Provider with a Switch and using StatefulWidget and its setState to refresh the widget (instead of using ChangeNotifierProvider and Consumer to "listen" and "localize" the widget rebuild to just the Switch and the Text label, which is perhaps a more typical use):
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class SwitchProviderPage extends StatefulWidget {
#override
_SwitchProviderPageState createState() => _SwitchProviderPageState();
}
class Database {
bool active = false;
void setActive(bool value) {
active = value;
}
}
class _SwitchProviderPageState extends State<SwitchProviderPage> {
#override
Widget build(BuildContext context) {
return Provider(
create: (context) => Database(),
child: Builder(
builder: (context) {
Database db = Provider.of<Database>(context, listen: false);
return Scaffold(
appBar: AppBar(
title: Text('Switch Field'),
),
body: SafeArea(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Active? ${db.active}'),
Switch(
onChanged: (val) { // ← remember to use val (bool)
print('Switch value: $val');
setState(() {
db.setActive(val);
// this sets the Switch setting on/off
});
},
value: db.active,
)
],
),
),
),
);
},
),
);
}
}
Note:
The use of Builder in above is only to make Scaffold be a child of Provider.
Otherwise, Scaffold would be a sibling, not a child, and Provider will not work. Since you wrap your entire app in your ChangeNotifierProvider, you don't need to do this. I needed to do this to get a self-contained example.
Stateless Version - ChangeNotifierProvider + Consumer
Here's a complete app example (copy paste into main.dart, replacing everything on page) using a StatelessWidget and the typical/common ChangeNotifierProvider & Consumer.
This version uses a mocked long duration async call when flipping Switch.
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() {
runApp(ChangeNotifierProvider<DatabaseListenable>(
create: (context) => DatabaseListenable(),
child: MyApp())
);
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Provider Demo App',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: StatelessSwitchProviderPage(),
);
}
}
class DatabaseListenable with ChangeNotifier {
bool active = false;
Future<void> setActive(bool value) async {
// Mock slow database call ↓
await Future.delayed(Duration(milliseconds: 500), () {
active = value;
print('Async DB call DONE.');
});
notifyListeners(); // ← causes Consumer to rebuild
}
}
class StatelessSwitchProviderPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Switch Provider Stateless'),
),
body: SafeArea(
child: Center(
child: Consumer<DatabaseListenable>(
builder: (context, db, child) => Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Active? ${db.active}'),
Switch(
onChanged: (val) {
print('Switch value: $val');
db.setActive(val);
},
value: db.active,
)
],
),
),
),
),
);
}
}
You have to add setState((){}); which rebuild the screen and display changes on your screen
I'm trying to change my icon after I tap on my List Item. I already tried different things: I tried the onTap method but the icon just does not want to change. I'm very new to flutter and I would love to find some help for my problem :). Here is my code.
I already searched for solutions but I didn't got it working in my project
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'To-Do List',
theme: ThemeData(
primaryColor: Colors.white,
brightness: Brightness.dark,
),
home: Scaffold(
appBar: AppBar(title: Text('To-Do List'),
backgroundColor: Colors.amber,
),
body: BodyLayout(),
),
);
}
}
class BodyLayout extends StatefulWidget {
#override
BodyLayoutState createState() {
return new BodyLayoutState();
}
}
class BodyLayoutState extends State<BodyLayout> {
// The GlobalKey keeps track of the visible state of the list items
// while they are being animated.
final GlobalKey<AnimatedListState> _listKey = GlobalKey();
// backing data
List<String> _data = [];
final _isdone = Set<String>();
// bool selected = false;
List<bool> selected = new List<bool>();
Icon notdone = Icon(Icons.check_box_outline_blank);
Icon done = Icon(Icons.check_box);
TextEditingController todoController = TextEditingController();
#override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
SizedBox(
height: 445,
child: AnimatedList(
// Give the Animated list the global key
key: _listKey,
initialItemCount: _data.length,
// Similar to ListView itemBuilder, but AnimatedList has
// an additional animation parameter.
itemBuilder: (context, index, animation) {
// Breaking the row widget out as a method so that we can
// share it with the _removeSingleItem() method.
return _buildItem(_data[index], animation);
},
),
),
TextField(
controller: todoController,
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: 'To-Do'
),
),
RaisedButton(
child: Text('Insert item', style: TextStyle(fontSize: 20)),
onPressed: () {
_insertSingleItem();
},
),
RaisedButton(
child: Text('Remove item', style: TextStyle(fontSize: 20)),
onPressed: () {
_removeSingleItem();
},
)
],
);
}
// This is the animated row with the Card.
Widget _buildItem(String item, Animation animation) {
final isdone = _isdone.contains(item);
selected.add(false);
return SizeTransition(
sizeFactor: animation,
child: Card(
child: ListTile(
title: Text(
item,
style: TextStyle(fontSize: 20),
),
trailing: Icon(
isdone ? Icons.check_box: Icons.check_box_outline_blank
),
onTap: (){
setState(() {
});
},
),
),
);
}
void _insertSingleItem() {
int insertIndex = 0;
setState(() {
_data.insert(0, todoController.text);
});
// Add the item to the data list.
// Add the item visually to the AnimatedList.
_listKey.currentState.insertItem(insertIndex);
}
void _removeSingleItem() {
int removeIndex = 0;
// Remove item from data list but keep copy to give to the animation.
String removedItem = _data.removeAt(removeIndex);
// This builder is just for showing the row while it is still
// animating away. The item is already gone from the data list.
AnimatedListRemovedItemBuilder builder = (context, animation) {
return _buildItem(removedItem, animation);
};
// Remove the item visually from the AnimatedList.
_listKey.currentState.removeItem(removeIndex, builder);
}
}```
You have already mentioned the icons above. You simply need to use them instead of declaring new ones again.
// This is the animated row with the Card.
Widget _buildItem(String item, Animation animation) {
final isdone = _isdone.contains(item);
selected.add(false);
return SizeTransition(
sizeFactor: animation,
child: Card(
child: ListTile(
title: Text(
item,
style: TextStyle(fontSize: 20),
),
trailing: isdone ? done: notdone, // use the icon variables you have already defined
onTap: (){
setState(() {
// add the item to _isdone set if it is not added and remove it if it is added when tapped on the list item
if(isdone) {
_isdone.remove(item);
} else {
_isdone.add(item);
}
});
},
),
),
);
}
In this code, I have added the item and removed the item in setSate() in the onTap(), so that whenever you tap the list item, _isdone Set gets updated and the build() is reloaded. Which makes your layout and data update itself every time you tap on the list item.
I have a problem and I cannot solve it and I did not find a source to solve it on Google, I have a page where I view a PDF file through a link, and I have a CircularProgressIndicator and I want to replace it with a progress bar showing the percentage of downloading the file, can I do that?
I have attached my code
import 'package:flutter/material.dart';
import 'package:flutter_plugin_pdf_viewer/flutter_plugin_pdf_viewer.dart';
class ReadPdf extends StatefulWidget {
final String value;
ReadPdf({Key key, this.value}) : super(key: key);
#override
_ReadPdfState createState() => _ReadPdfState();
}
class _ReadPdfState extends State<ReadPdf>{
bool _isloading = false, _isInit = true;
PDFDocument document;
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
backgroundColor: Color.fromRGBO(58, 66, 86, 1.0),
body: Container(
child: Column(
children: <Widget>[
Expanded(child:Center(
child: _isInit? MaterialButton(child: Text('Go'), onPressed: () {_loadFromURL(widget.value);},
color: Color.fromRGBO(64, 75, 96, .9),
textColor: Colors.white,
) : _isloading? Center(child: CircularProgressIndicator(),) : PDFViewer(document: document,indicatorBackground: Colors.deepPurple,),
),),
Row(
mainAxisSize: MainAxisSize.max,
children: <Widget>[
],
),
],
),
),
),
);
}
_loadFromURL(String url) async{
setState(() {
_isInit = false;
_isloading = true;
});
document = await PDFDocument.fromURL('${url}'); setState(() {
_isloading = false;
});
}
}
I have an app with the same feature, I used Dio this package supports downloading a file to your phone.
All you need to do is
Dio dio = Dio();
dio.download("*YOUR URL WHERE YOU WANT TO DOWNLOAD A FILE*",
"*YOUR DESTINATION PATH*", onReceiveProgress: (rec, total) {
print("Downloading " + ((rec / total) * 100).toStringAsFixed(0) + "%");
});
Never used this for pdf, but I've tried it for NetworkImage().
Not sure if it'll help. But you can just try it if there's a way to use loadingBuilder in your code.
Image.network(
imageUrl,
fit: BoxFit.cover,
loadingBuilder: (BuildContext context, Widget child,
ImageChunkEvent loadingProgress) {
if (loadingProgress == null)
return child;
else {
return Center(
child: CircularProgressIndicator(
value: loadingProgress.expectedTotalBytes != null
? loadingProgress.cumulativeBytesLoaded /
loadingProgress.expectedTotalBytes
: null,
),
);
}
},
);
u can use flutter_cached_pdfview
and this an example to view a pdf from URL and cache it with placeholder
u can replace placeholder with any widget like CircularProgressIndicator
PDF().cachedFromUrl(
'http://africau.edu/images/default/sample.pdf',
placeholder: (progress) => Center(child: Text('$progress %'))
)
take a look https://pub.dev/packages/flutter_cached_pdfview
I'm building an app for training in Flutter and I'm actually stuck in the filter functionality.
I have a ListView where I fetch data from TheMovieDB API and a ModalBottomSheet with three FilterChips for selecting the filter criteria (popular, top rated and latest movies).
And here's where I'm stuck. I want to call the "_loadNextPage()" method when the user presses the "Done" button in the ModalBottomSheet through "performUpdate()" but I can't do it because they're not in the same class.
I'll post the code down below for better understanding.
class _HomePageState extends State<HomePage> {
RequestProvider _requestProvider = new RequestProvider();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("FluttieDB"),
actions: <Widget>[
IconButton(
icon: Icon(Icons.filter_list),
onPressed: () => buildFilterBottomSheet(),
)
],
),
body: MovieList(_requestProvider, _currentFilter),
);
}
void buildFilterBottomSheet() {
showModalBottomSheet(
context: context,
builder: (builder) {
return Container(
height: 150.0,
decoration: BoxDecoration(color: Colors.white),
child: Column(
children: <Widget>[
buildFilterTitle(context),
Expanded(
child: _FilterChipRow(),
),
],
),
);
});
}
Widget buildFilterTitle(BuildContext context) {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 6.0),
alignment: Alignment.centerLeft,
height: 46.0,
decoration: BoxDecoration(color: Colors.blue),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Text(
"Filter by",
style: TextStyle(color: Colors.white, fontSize: 20.0),
),
OutlineButton(
onPressed: () => performUpdate(context),
padding: const EdgeInsets.all(0.0),
shape: const StadiumBorder(),
child: Text(
"Done",
style: TextStyle(color: Colors.white),
),
),
],
),
);
}
void performUpdate(BuildContext context) {
MovieList _movieList = new MovieList(_requestProvider, _currentFilter);
_movieList.createState()._loadNextPage();
Navigator.pop(context);
}
}
class MovieList extends StatefulWidget {
MovieList(this.provider, this.currentFilter, {Key key}) : super(key: key);
final RequestProvider provider;
final String currentFilter;
#override
_MovieListState createState() => new _MovieListState();
}
class _MovieListState extends State<MovieList> {
List<Movie> _movies = List();
int _pageNumber = 1;
LoadingState _loadingState = LoadingState.LOADING;
bool _isLoading = false;
_loadNextPage() async {
_isLoading = true;
try {
var nextMovies = await widget.provider
.provideMedia(widget.currentFilter, page: _pageNumber);
setState(() {
_loadingState = LoadingState.DONE;
_movies.addAll(nextMovies);
_isLoading = false;
_pageNumber++;
});
} catch (e) {
_isLoading = false;
if (_loadingState == LoadingState.LOADING) {
setState(() => _loadingState = LoadingState.ERROR);
}
}
}
#override
void initState() {
super.initState();
_loadNextPage();
}
#override
Widget build(BuildContext context) {
switch (_loadingState) {
case LoadingState.DONE:
return ListView.builder(
itemCount: _movies.length,
itemBuilder: (BuildContext context, int index) {
if (!_isLoading && index > (_movies.length * 0.7)) {
_loadNextPage();
}
return MovieListItem(_movies[index]);
});
case LoadingState.ERROR:
return Center(
child: Text("Error retrieving movies, check your connection"));
case LoadingState.LOADING:
return Center(child: CircularProgressIndicator());
default:
return Container();
}
}
}
As you can see, I did some experiments in the performUpdate() but it doesn't refresh the ListView with the selected option in the filters and I don't think it's the best way to achieve what I want.
Thanks and sorry if the question is a bit dumb. I'm a little bit newbie in Flutter.
Redux is a great state management library that originated with React and JS, but has been ported to Dart, and has a flutter specific library as well. Redux is a very powerful framework which uses a pub/sub system to allow your view to subscribe to changes to the model, while using a system of "actions" and "reducers" to update the model.
A great tutorial for getting up and running with Redux in Flutter can be found here
Alternatively you could look into the scoped model, which is another state management library for flutter. The scoped model is less capable, but for simple use cases may be more than adequate.
Further reading:
Understand and choose a state management solution
You Might Not Need Redux
in the Codelab English words example...
https://flutter.io/get-started/codelab/
The iOS Navigation transition is horizontal.. as you would expect a Segue to act in a UINavigationController. Right to left... Pops are left to right.
ANDROID, the same example is VERTICAL, Bottom to Top. Pops are Top to bottom.
MY QUESTION... how would I force a Horizontal transition in ANDROID so it behaves like iOS? I suspect I will have to use MaterialPageRoute
/*
Nguyen Duc Hoang(Mr)
Programming tutorial channel:
https://www.youtube.com/c/nguyenduchoang
Flutter, React, React Native, IOS development, Swift, Python, Angular
* */
import 'package:flutter/material.dart';
import 'package:english_words/english_words.dart';
//Define "root widget"
void main() => runApp(new MyApp());//one-line function
//StatefulWidget
class RandomEnglishWords extends StatefulWidget {
#override
State<StatefulWidget> createState() {
// TODO: implement createState
return new RandomEnglishWordsState();//return a state's object. Where is the state's class ?
}
}
//State
class RandomEnglishWordsState extends State<RandomEnglishWords>{
final _words = <WordPair>[];//Words displayed in ListView, 1 row contains 1 word
final _checkedWords = new Set<WordPair>();//set contains "no duplicate items"
#override
Widget build(BuildContext context) {
// TODO: implement build
//Now we replace this with a Scaffold widget which contains a ListView
return new Scaffold(
appBar: new AppBar(
title: new Text("List of English words"),
actions: <Widget>[
new IconButton(icon: new Icon(Icons.list),
onPressed: _pushToSavedWordsScreen)
],
),
body: new ListView.builder(itemBuilder: (context, index) {
//This is an anonymous function
//index = 0, 1, 2, 3,...
//This function return each Row = "a Widget"
if (index >= _words.length) {
_words.addAll(generateWordPairs().take(10));
}
return _buildRow(_words[index], index);//Where is _buildRow ?
}),
);
}
_pushToSavedWordsScreen() {
// print("You pressed to the right Icon");
//To navigate, you must have a "route"
final pageRoute = new MaterialPageRoute(builder: (context) {
//map function = Convert this list to another list(maybe different object's type)
//_checkedWords(list of WordPair) => map =>
// converted to a lazy list(Iterable) of ListTile
final listTiles = _checkedWords.map( (wordPair) {
return new ListTile(
title: new Text(wordPair.asUpperCase,
style: new TextStyle(fontSize: 20.0, fontWeight: FontWeight.bold),),
);
});
//Now return a widget, we choose "Scaffold"
return new Scaffold(
appBar: new AppBar(
title: new Text("Checked words"),
),
body: new ListView(children: listTiles.toList(),),//Lazy list(Iterable) => List
);
});
Navigator.of(context).push(pageRoute);
}
Widget _buildRow(WordPair wordPair, int index) {
//This widget is for each row
final textColor = index % 2 == 0 ? Colors.red : Colors.blue;
final isChecked = _checkedWords.contains(wordPair);
return new ListTile(
//leading = left, trailing = right. Is is correct ? Not yet
leading: new Icon(
isChecked ? Icons.check_box : Icons.check_box_outline_blank,
color: textColor,
),
title: new Text(
wordPair.asUpperCase,
style: new TextStyle(fontSize: 18.0, color: textColor),
),
onTap: () {
setState(() {
//This is an anonymous function
if (isChecked) {
_checkedWords.remove(wordPair);//Remove item in a Set
} else {
_checkedWords.add(wordPair);//Add item to a Set
}
});
},
);
}
}
class MyApp extends StatelessWidget {
//Stateless = immutable = cannot change object's properties
//Every UI components are widgets
#override
Widget build(BuildContext context) {
//build function returns a "Widget"
return new MaterialApp(
title: "This is my first Flutter App",
home: new RandomEnglishWords()
);//Widget with "Material design"
}
}
First of all about MaterialPageRoute does not help with your case. Here is the official explanation for it:
The MaterialPageRoute is handy because it transitions to the new
screen using a platform-specific animation.
And those animations you see are the platform-specific animations.
If you want to implement a custom animation, you need to implement it manually by using PageRouteBuilder. Here is how you can do it.
Here is a modified version of your _pushToSavedWordsScreen which does the right to left transition. Tested on Google Pixel.
final pageRoute = new PageRouteBuilder(
pageBuilder: (BuildContext context, Animation animation,
Animation secondaryAnimation) {
// YOUR WIDGET CODE HERE
final listTiles = _checkedWords.map((wordPair) {
return new ListTile(
title: new Text(
wordPair.asUpperCase,
style: new TextStyle(fontSize: 20.0, fontWeight: FontWeight.bold),
),
);
});
//Now return a widget, we choose "Scaffold"
return new Scaffold(
appBar: new AppBar(
title: new Text("Checked words"),
),
body: new ListView(
children: listTiles.toList(),
), //Lazy list(Iterable) => List
);
},
transitionsBuilder: (BuildContext context, Animation<double> animation,
Animation<double> secondaryAnimation, Widget child) {
return SlideTransition(
position: new Tween<Offset>(
begin: const Offset(1.0, 0.0),
end: Offset.zero,
).animate(animation),
child: new SlideTransition(
position: new Tween<Offset>(
begin: Offset.zero,
end: const Offset(1.0, 0.0),
).animate(secondaryAnimation),
child: child,
),
);
},
);
Navigator.of(context).push(pageRoute);
This is the modified code for the new Navigation. Android and iOS both Navigate Horizontally.
Origianl code: https://flutter.io/get-started/codelab/[1]
pubspec.yaml... Don't forget to add :
dependencies:
flutter:
sdk: flutter
english_words: ^3.1.0 #version 3.1.0 or above
UPDATED CODE: main.dart.
/*
Nguyen Duc Hoang(Mr)
Programming tutorial channel:
https://www.youtube.com/c/nguyenduchoang
Flutter, React, React Native, IOS development, Swift, Python, Angular
* */
import 'package:flutter/material.dart';
import 'package:english_words/english_words.dart';
//Define "root widget"
void main() => runApp(new MyApp());//one-line function
//StatefulWidget
class RandomEnglishWords extends StatefulWidget {
#override
State<StatefulWidget> createState() {
// TODO: implement createState
return new RandomEnglishWordsState();//return a state's object. Where is the state's class ?
}
}
//State
class RandomEnglishWordsState extends State<RandomEnglishWords> {
final _words = <WordPair>[
]; //Words displayed in ListView, 1 row contains 1 word
final _checkedWords = new Set<WordPair>(); //set contains "no duplicate items"
#override
Widget build(BuildContext context) {
// TODO: implement build
//Now we replace this with a Scaffold widget which contains a ListView
return new Scaffold(
appBar: new AppBar(
title: new Text("List of English words"),
actions: <Widget>[
new IconButton(icon: new Icon(Icons.list),
onPressed: _pushToSavedWordsScreen)
],
),
body: new ListView.builder(itemBuilder: (context, index) {
//This is an anonymous function
//index = 0, 1, 2, 3,...
//This function return each Row = "a Widget"
if (index >= _words.length) {
_words.addAll(generateWordPairs().take(10));
}
return _buildRow(_words[index], index); //Where is _buildRow ?
}),
);
}
_pushToSavedWordsScreen() {
// print("You pressed to the right Icon");
//To navigate, you must have a "route"
//======================================================================
//======= original solution - ANDROID transitions Vertically
// final pageRoute = new MaterialPageRoute(builder: (context) {
// //map function = Convert this list to another list(maybe different object's type)
// //_checkedWords(list of WordPair) => map =>
// // converted to a lazy list(Iterable) of ListTile
// final listTiles = _checkedWords.map( (wordPair) {
// return new ListTile(
// title: new Text(wordPair.asUpperCase,
// style: new TextStyle(fontSize: 20.0, fontWeight: FontWeight.bold),),
// );
// });
// //Now return a widget, we choose "Scaffold"
// return new Scaffold(
// appBar: new AppBar(
// title: new Text("Checked words"),
// ),
// body: new ListView(children: listTiles.toList(),),//Lazy list(Iterable) => List
// );
// });
// Navigator.of(context).push(pageRoute);
// }
//=========== OLD solution... ANDROID transitions Vertically
//==================================================================
//==================================================================
//=========== new solution... transition Horizontal
final pageRoute = new PageRouteBuilder(
pageBuilder: (BuildContext context, Animation animation,
Animation secondaryAnimation) {
// YOUR WIDGET CODE HERE
final listTiles = _checkedWords.map((wordPair) {
return new ListTile(
title: new Text(
wordPair.asUpperCase,
style: new TextStyle(fontSize: 20.0, fontWeight: FontWeight.bold),
),
);
});
//Now return a widget, we choose "Scaffold"
return new Scaffold(
appBar: new AppBar(
title: new Text("Checked words"),
),
body: new ListView(
children: listTiles.toList(),
), //Lazy list(Iterable) => List
);
},
transitionsBuilder: (BuildContext context, Animation<double> animation,
Animation<double> secondaryAnimation, Widget child) {
return SlideTransition(
position: new Tween<Offset>(
begin: const Offset(1.0, 0.0),
end: Offset.zero,
).animate(animation),
child: new SlideTransition(
position: new Tween<Offset>(
begin: Offset.zero,
end: const Offset(1.0, 0.0),
).animate(secondaryAnimation),
child: child,
),
);
},
);
Navigator.of(context).push(pageRoute);
}
//========= end of solution
//=============================================================
Widget _buildRow(WordPair wordPair, int index) {
//This widget is for each row
final textColor = index % 2 == 0 ? Colors.red : Colors.blue;
final isChecked = _checkedWords.contains(wordPair);
return new ListTile(
//leading = left, trailing = right. Is is correct ? Not yet
leading: new Icon(
isChecked ? Icons.check_box : Icons.check_box_outline_blank,
color: textColor,
),
title: new Text(
wordPair.asUpperCase,
style: new TextStyle(fontSize: 18.0, color: textColor),
),
onTap: () {
setState(() {
//This is an anonymous function
if (isChecked) {
_checkedWords.remove(wordPair); //Remove item in a Set
} else {
_checkedWords.add(wordPair); //Add item to a Set
}
});
},
);
}
}
class MyApp extends StatelessWidget {
//Stateless = immutable = cannot change object's properties
//Every UI components are widgets
#override
Widget build(BuildContext context) {
//build function returns a "Widget"
return new MaterialApp(
title: "This is my first Flutter App",
home: new RandomEnglishWords()
);//Widget with "Material design"
}
}