flutter, Navigation horizontal - android

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"
}
}

Related

Show Image A or B - Flutter

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);
}
}

Flutter changing Icon onTap in my animated List

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.

How To Repeat The Widget In Every 5 Seconds in Flutter?

I want to refresh the charts widget in every 5 seconds, But, here I am doing something wrong, but I do not know what I'm missing or what I did wrong. I only got the white screen in a view.
I have a dummy data in createRandomData
Calling the createRandomData
factory PieChartScreen.withRandomData(){
Timer.periodic(Duration(seconds: 5), (timer) {
return PieChartScreen(
createRandomData(),
);
});
}
Full SourceCode at PieChartScreen
import 'package:flutter/material.dart';
import 'package:charts_flutter/flutter.dart' as charts;
import 'dart:math';
import 'dart:async';
class PieChartScreen extends StatelessWidget {
List<charts.Series> seriesList;
final bool animate;
PieChartScreen(this.seriesList, {this.animate});
factory PieChartScreen.withSampleData(){
return new PieChartScreen(
createSampleData(),
animate: false,
);
}
factory PieChartScreen.withRandomData(){
Timer.periodic(Duration(seconds: 5), (timer) {
return PieChartScreen(
createRandomData(),
);
});
}
static List<charts.Series<Spending, int>> createRandomData() {
Timer.periodic(Duration(seconds: 5), (timer) {
final random = new Random();
final data = [
new Spending(2014, random.nextInt(1000000)),
new Spending(2015, random.nextInt(1000000)),
new Spending(2016, random.nextInt(1000000)),
new Spending(2017, random.nextInt(1000000)),
new Spending(2018, random.nextInt(1000000)),
new Spending(2019, random.nextInt(1000000)),
];
return [
new charts.Series(id: 'Spending',
data: data,
domainFn: (Spending sp, _) => sp.year,
measureFn: (Spending sp, _) => sp.spending,
labelAccessorFn: (Spending sp, _) => '${sp.year} : ${sp.spending}'
)
];
// Timer timer = new Timer.periodic(Duration(seconds: 2));
});
}
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: AppBar(
title: Text("Pie Chart Screen")
),
body: Padding(
padding: EdgeInsets.all(8.0),
child: new charts.PieChart(seriesList,
animate: animate,
defaultRenderer: new charts.ArcRendererConfig(
arcRendererDecorators: [
new charts.ArcLabelDecorator(
labelPosition: charts.ArcLabelPosition.auto)
]),
),
),
);
}
static List<charts.Series<Spending, int>> createSampleData() {
final data = [
new Spending(2014, 5),
new Spending(2014, 25),
new Spending(2014, 50),
new Spending(2014, 100),
new Spending(2014, 75),
new Spending(2014, 25),
];
return [
new charts.Series(id: 'Spending',
data: data,
domainFn: (Spending sp, _) => sp.year,
measureFn: (Spending sp, _) => sp.spending,
labelAccessorFn: (Spending sp, _) => '${sp.year} \n : ${sp.spending}'
)
];
}
}
class Spending {
final int year;
final int spending;
Spending(this.year, this.spending);
}
I hope the widget can be refreshed every 5 seconds.
I would change my PieChartScreen to a Stateful Widget.
class PieChartScreen extends StatelessWidget {
to something like this pseudo code:
class PieChartScreen extends StatefulWidget {
List<charts.Series> seriesList;
.....
int changeCounter=0;
.....
#override
PieChartScreenState createState() => PieChartScreenState();
}
class PieChartScreenState extends State<PieChartScreen> {
PieChartScreenState() {}
....
factory PieChartScreen.withRandomData(){
Timer.periodic(Duration(seconds: 5), (timer) {
return PieChartScreen(
setState (() {. // NOTE
createRandomData(),
changeCounter=changeCounter+1; // NOTE
}); // NOTE
);
});
}
....
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: AppBar(
title: Text("Pie Chart Screen")
),
body: changeCounter == 0 ? CircularProgressIndicator() : // NOTE
Padding(
padding: EdgeInsets.all(8.0),
child: new charts.PieChart(seriesList,
animate: animate,
defaultRenderer: new charts.ArcRendererConfig(
arcRendererDecorators: [
new charts.ArcLabelDecorator(
labelPosition: charts.ArcLabelPosition.auto)
]),
),
),
);
}
}
Notice the change of PieChartScreen to a StateFulWidget and the inclusion of the changeCounter variable which, in this case I just used an int for examples sake, is used in the setState construct to indicate a change in the state of the data which should be reflected in the Widget tree and in the Build method itself.

Flutter - Dialog start new Activity

I am developing an app that is counting points, after the teams have been created. After the teams are created, an AlertDialog pops up and displays the names. Then it should be possible to click on a button to open a new activity. That activity should not be connected to the previous activity. Does anybody has an idea, how this could be done?
Here is the code-snippet of the dialog activity:
import 'dart:math';
import 'package:flutter/material.dart';
import 'punktezaehler.dart';
class Team_Dialog extends StatefulWidget{
final List<String> team_namen;
Team_Dialog(this.team_namen);
#override
State<StatefulWidget> createState() => new _TeamDialogState(team_namen);
}
class _TeamDialogState extends State<Team_Dialog>{
final List<String> team_namen;
_TeamDialogState(this.team_namen);
#override
Widget build(BuildContext context) {
return new AlertDialog(
content: new SingleChildScrollView(
child: new ListBody(
children: List.generate(1, (index){
return Column(
children: <Widget>[
new Row(
children: <Widget>[
Text("Team 1: ", style: TextStyle(fontFamily: "Roboto")),
Text(team_namen[0] + " und " + team_namen[1])
],
),
new Row(
children: <Widget>[
Text("Team 2: "),
Text(team_namen[2] + " und " + team_namen[3])
],
)
],
);
})
),
),
actions: <Widget>[
new FlatButton(
color: Colors.red,
splashColor: Colors.red[900],
onPressed: (){
Navigator.of(context).pop();
},
child: new Text("Abort", style: TextStyle(color: Colors.white),)
),
new IconButton(
icon: Icon(Icons.shuffle),
onPressed: (){
shuffle(team_namen);
setState(() {
});
}
),
new FlatButton(
color: Colors.green,
splashColor: Colors.green[800],
onPressed: () => , //After click it should start new Activity
child: new Text("Start Game", style: TextStyle(color: Colors.white))
)
],
);
}
List shuffle(List items) {
var random = new Random();
for (var i = items.length - 1; i > 0; i--) {
var n = random.nextInt(i + 1);
var temp = items[i];
items[i] = items[n];
items[n] = temp;
}
return items;
}
}
It would be awesome if someone has an idea :D
Actually when you are talking about Flutter think about pages, not activities. It should be something like :
Navigator.push(context,
MaterialPageRoute(builder: (context) => SecondScreen()),);
SecondScreen is another widget with its own Widget build(BuildContext context) method where you will declare what to have on this page.
In case you want to return back, you can do it with:
Navigator.pop(context);
Source documentation
You can also use named routes for navigation. Example:
MaterialApp(
// Start the app with the "/" named route. In our case, the app will start
// on the FirstScreen Widget
initialRoute: '/',
routes: {
// When we navigate to the "/" route, build the FirstScreen Widget
'/': (context) => FirstScreen(),
// When we navigate to the "/second" route, build the SecondScreen Widget
'/second': (context) => SecondScreen(),
},
);
And something like:
Navigator.pushNamed(context, '/second');
Documentation
It's not a good practice to use Navigator.push() or Navigator.pushNamed() and then remove the back button. Because the page that you are leaving from will remain in the pages stack.
What you actually should use is Navigator.pushReplacement() if you don't want the user to be able to go back to the previous page.
And if you are doing it from a dialog, you should pop the dialog first and then push the next page.

Replace widgets like fragments in Flutter

I'm new to Flutter.
I have an app with 2 sub widgets (2 fragments in Android), and when i clicked next button in WidgetA, I want to replace (or push) that widget into WidgetChildA, like push (or replace) fragments in Android. But instead of that, I got a fullscreen widget like a normal screen in Flutter.
Here is my code:
import 'package:flutter/material.dart';
class DemoFragment extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return new DemoFragmentState();
}
}
class DemoFragmentState extends State<DemoFragment> {
#override
Widget build(BuildContext context) {
print(context.toString() + context.hashCode.toString());
return new Scaffold(
appBar: new AppBar(title: new Text("Demo fragment")),
body: new Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
new FragmentA(),
new FragmentB()
],
),
);
}
}
class FragmentA extends StatelessWidget {
#override
Widget build(BuildContext context) {
print(context.toString() + context.hashCode.toString());
return new Center(
child: new Column(
children: <Widget>[
new Text("Fragment A"),
new RaisedButton(
child: new Text("next"),
onPressed: () {
print(context.toString() + context.hashCode.toString());
Navigator.of(context).push(new PageRouteBuilder(
opaque: true,
transitionDuration: const Duration(milliseconds: 0),
pageBuilder: (BuildContext context, _, __) {
return new FragmentChildA();
}));
/*showDialog(
context: context,
builder: (_) => new AlertDialog(
title: new Text("Hello world"),
content: new Text("this is my content"),
));*/
})
],
),
);
}
}
class FragmentB extends StatelessWidget {
#override
Widget build(BuildContext context) {
print(context.toString() + context.hashCode.toString());
return new Center(
child: new Column(
children: <Widget>[
new Text("Fragment B"),
new RaisedButton(
child: new Text("next"),
onPressed: () {
print(context.toString() + context.hashCode.toString());
Navigator.of(context).push(new PageRouteBuilder(
opaque: true,
transitionDuration: const Duration(milliseconds: 0),
pageBuilder: (BuildContext context, _, __) {
return new FragmentChildB();
}));
})
],
));
}
}
class FragmentChildA extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new Scaffold(
body: new Center(
child: new Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[new Text("Fragment Child A")],
)));
}
}
class FragmentChildB extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new Scaffold(
body: new Center(
child: new Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[new Text("Fragment Child B")],
)));
}
}
Screenshots:
Home page
After clicked
I'm not sure if you can use the router to replace just the part of a view; but you could conditionally change which Widget you render in the build method, like this:
children: <Widget>[
someCondition ? new FragmentA() : new FragmentChildA(),
new FragmentB()
],
Then you just need to set someCondition by using setState in the stateful widget:
setState(() => someCondition = true);
If you want to do this from inside FragmentA you could allow it to have the function passed into its constructor:
new FragmentA(
onPress: setState(() => someCondition = true)
)
However, it might be better to encapsulate all of this logic inside a single widget so this logic isn't all hanging around in the parent. You could make a single StatefulWidget for FragementA which keeps track of which stage you're on, and then in its build method renders the correct child widget, something like:
build() {
switch(stage) {
Stages.Stage1:
return new Stage1(
onNext: () => setState(() => stage = Stages.Stage2);
);
Stages.Stage2:
return new Stage1(
onPrevious: () => setState(() => stage = Stages.Stage1);
);
}
}
You could simply use a MaterialApp widget with the CupertinoPageTransitionsBuilder as pageTransitionTheme like
MaterialApp(
theme: ThemeData(
pageTransitionsTheme: PageTransitionsTheme(builders: {
TargetPlatform.iOS: CupertinoPageTransitionsBuilder(),
TargetPlatform.android: SlideRightPageTransitionsBuilder(),
}),
initialRoute: "fragment1",
routes: <String, WidgetBuilder>{
"fragment1": (BuildContext context) => Fragment1(),
"fragment2": (BuildContext context) => Fragment2(),
}
...
),
Then in fragment 1 you simply use the following to navigate to the other fragment with a slide animation
Navigator.of(context).pushReplacementNamed("fragment2");
Well, I found out the way to handle this case for a few months, but I just forgot to answer this question.
The solution is wrapping your Widget with a new Navigator.
You can see the video example here
And the simple demo for it here
The downside of this solution is sometimes, the keyboard is not showing as my intention.
ok I'm going to be doing this the same way google does it with the bottom navigation bar, I don't see this as the most performant but it works
class MainFabContainer extends StatefulWidget {
MainFabContainer({
Key key,
}) : super(key: key);
#override
State<StatefulWidget> createState() {
return MainFabContainerState();
}
}
class MainFabContainerState extends State<MainFabContainer> {
String title = "Title";
int _currentIndex = 0;
final List<int> _backstack = [0];
#override
Widget build(BuildContext context) {
//each fragment is just a widget which we pass the navigate function
List<Widget> _fragments =[Fragment1(navigate: navigateTo),Fragment2(navigate: navigateTo,),Fragment3(navigate: navigateTo,)];
//will pop scope catches the back button presses
return WillPopScope(
onWillPop: () {
customPop(context);
},
child: Scaffold(
drawer: drawer(),
appBar: AppBar(
title: Text(title),
),
body: Column(
children: <Widget>[
Expanded(
child: _fragments[_currentIndex],
),
],
),
),
);
}
void navigateTo(int index) {
_backstack.add(index);
setState(() {
_currentIndex = index;
});
}
void navigateBack(int index) {
setState(() {
_currentIndex = index;
});
}
customPop(BuildContext context) {
if (_backstack.length - 1 > 0) {
navigateBack(_backstack[_backstack.length - 1]);
} else {
_backstack.removeAt(_backstack.length - 1);
Navigator.pop(context);
}
}
//this method could be called by the navigate and navigate back methods
_setTitle(String appBarTitle) {
setState(() {
title = appBarTitle;
});
}
}

Categories

Resources