在Codelab英语单词示例中...
https://flutter.io/get-started/codelab/
iOS导航过渡是水平 ..,就像您希望Segue在UINavigationController中起作用一样。从右到左...流行音乐从左到右。
ANDROID,相同的示例是VERTICAL,从下至上。持久性有机污染物从上到下。
我的问题...我应如何在ANDROID中强制进行 Horizontal 过渡,使其表现得像iOS?我怀疑我将不得不使用 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"
}
}
答案 0 :(得分:2)
首先,关于MaterialPageRoute
的问题无济于事。这是它的官方解释:
MaterialPageRoute很方便,因为它可以过渡到新的 屏幕使用平台特定的动画。
您看到的那些动画是特定于平台的动画。
如果要实现自定义动画,则需要使用PageRouteBuilder
手动实现。这是您的操作方法。
这是_pushToSavedWordsScreen
的修改版本,它从右向左过渡。在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);
答案 1 :(得分:0)
这是新导航的修改后的代码。 Android和iOS都可以水平导航。 原始代码:https://flutter.io/get-started/codelab/[1]
pubspec.yaml ...不要忘记添加:
dependencies:
flutter:
sdk: flutter
english_words: ^3.1.0 #version 3.1.0 or above
更新的代码: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"
}
}