面向React Native开发者的Flutter——Props、本地存储、路由、手势监听、HTTP请求、表单输入
一、Props
在React Native中,大多数组件在创建时都可以使用不同的参数或属性进行自定义,称为props
。这些参数可以在子组件中使用this.props
// React Native
class CustomCard extends React.Component {
render() {
<View>
<Text> Card {this.props.index} </Text>
<Button
title='Press'
onPress={() => this.props.onPress(this.props.index)}
/>
</View>
}
}
class App extends React.Component {
onPress = index => {
console.log('Card', index);
};
render() {
return (
<View>
<FlatList
data={[ ... ]}
renderItem={({ item }) => (
<CustomCard onPress=({this.onPress} index={item.key}/>
)};
/>
</View>
);
}
}
在Flutter中,您可以分配一个布局变量或函数,该变量或函数final
有在参数化构造函数中接手到的属性。
import 'package:flutter/material.dart';
class CustomCard extends StatelessWidget {
const CustomCard({
Key? key,
required this.index,
required this.onPress,
}) : super(key: key);
final int index;
final void Function() onPress;
@override
Widget build(BuildContext context) {
return Card(
child: Column(
children: <Widget>[
Text('Card $index'),
TextButton(
onPressed: onPress,
child: const Text('Press'),
),
],
),
);
}
}
class UseCard extends StatelessWidget {
final int index;
const UseCard({Key? key, required this.index}) : super(key: key);
@override
Widget build(BuildContext context) {
return CustomCard(
index: index,
onPress: () {
print('Card $index');
});
}
}
- Android
- iOS
二、本地存储
如果您不需要存储大量数据,并且不需要结构,您可以使用shared_preferences
它来读取和写入基本数据类型的持久键值对:布尔值、浮点数、整数、长整数和字符串。
2.1、如何存储应用程序全局的持久键值对?
在React Native中,您可以使用组件的setItem
和getItem
函数AsyncStorage
来存储和检索对应用程序而言持久且全局的数据。
// React Native
await AsyncStorage.setItem('counterkey', json.stringf(++this.state.counter));
AsyncStorage.getItem('counterkey').then(value => {
if (value != null ) {
this.setState({ counter: value });
}
});
在Flutter中,使用shared_preferences
插件来存储和检索对应用程序而言持久且全局的键值数据。该shared_preferences
插件包装iOS的NSUserDefaults
和Android的SharedPreferences
,为简单数据持久存储。要使用该插件,请将shared_preferences
其作为依赖项pubspec.yaml
文件中,然后将包导入到Dart文件中。
dependencies:
flutter:
sdk: flutter
shared_preferences: ^2.0.13
import 'package:shared_preferences/shared_preferences.dart';
要实现持久数据,请使用SharedPreferences
类提供的setter方法。Setter方法可用于各种基本类型,例如setInt
、setBool
和setString
。要读取数据,请使用SharedPreferences
类提供的适当的getter方法。对于每个setter都有一个对应的getter方法,例如getInt
、getBool
和getString
。
Future<void> updateCounter() async {
final prefs = await SharedPreferences.getInstance();
int? counter = prefs.getInt('counter');
if (counter is int) {
await prefs.setInt('counter', ++counter);
}
setState(() {
_counter = counter;
});
}
三、路由
大多数应用程序包含多个屏幕,用于显示不同类型的信息。例如,您可能有一个显示图像的产品屏幕,用户可以在其点击产品图像以在屏幕上获取有关该产品的更多信息。
在Android中,新屏幕是Activity。在iOS中,新屏幕是新的ViewController。在Flutter中,屏幕就是Widget! 要导航到Flutter中的新屏幕,请使用Navigator小部件。
3.1、如何在屏幕之间导航?
在React Native中,主要有三个导航器:StackNavigator、TabNavigator和DrawerNavigator。每个都提供了一种配置和定义屏幕的方法。
// React Native
const MyApp = TabNavigator(
{ Home: { screen: HomeScreen }, Natifications: { screen: tabNavScreen} },
{ tabBarOptions: { activeTintColor: '#e91e63' }}
);
const SimpleApp = StackNavigator({
Home: { screen: MyApp },
stackScreen: { screen:StackScreen }
});
export default (MyApp1 = DrawerNavigaror({
Home: {
screen: SimpleApp
},
Screen2: {
screen: drawerScreen
}
}));
在Flutter中,有两个主要哦的小部件用于在屏幕之间导航:
Router
是应用程序平或页面的抽象。Navigator
是管理路由的小部件。
Navigator
被定义为小部件,它管理一组具有堆栈规则的小部件。导航器管理Route
对象堆栈并提供管理堆栈的方法,例如Navigator.push
和Navigator.pop
。路由列表可以在MaterialApp
小部件中指定,或者它们可以动态构建,例如,在Hero animation。以下示例指定MaterialApp
小部件中的命名路由。
注意: 对于大多数应用程序,不再推荐使用命名路由。有关详细信息,请参阅 导航概述页面中的限制。
import 'package:flutter/material.dart';
class NavigationApp extends StatelessWidget {
// This widget is the root of your applicatuib.
const NavigationApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
// ...
routes: <String, WidgetBuilder>{
'/a': (context) => const UsualNavScreen(),
'/b': (context) => const DrawerNavScreen().
},
),
);
}
}
要导航到命名路由,该Navigator.of()
方法用于指定BuildContext
(小部件树中小部件位置的句柄)。路由的名称被传递给pushNamed
函数以导航到指定的路由。
Navigatoor.of(context).pushNamed('/a');
您还可以使用push方法,Navigator
该方法将given添加Route
到最紧密包围given的导航器的历史记录中BuildContext
,并过渡到它。在下面的示例中,MaterialPageRoute
小部件是一个模拟路由,它用平台自适应转换替换整个屏幕。它需要一个WidgetBuilder
作为必须的参数。
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const UsualNavScreen(),
),
);
3.2、如何使用标签到哦行和抽屉导航?
在Material Design应用程序中,Flutter导航有两个主要选项:选项卡和抽屉。当没有足够的空间来支持选项卡时,抽屉提供了一个很好的选择。
3.2.1、标签导航
在React Native中,createBottomTabNavigator
用于TabNavigation
选项卡和用于选项卡导航。
// React Native
import { createBottomTabNavigator } from 'react-navigation';
const MyApp = TabNavigator(
{ Home: { screen: HomeScreen }, Notifications: { screen: tabNavScreen } },
{ tabBarOptions: { activeTintColor: '#e91e63' } }
);
Flutter为抽屉和选项卡导航提供了几个专门的小部件:
TabController
: 协调TabBar和TabBarView之间的选项卡选择。
TabBar
: 显示一行水平的选项卡。
Tab
: 创建material design Tab bar。
TabBarView
: 显示与当前所选选项卡对应的小部件。
import 'package:flutter/material.dart';
class _MyAppState extends State<MyApp> with SingleTickerProviderStateMixin {
late TabController controller = TabController(length: 2, vsync: this);
@override
Widget build(BuildContext context) {
return TabBar(
controller: controller,
tabs: const <Tab>[
Tab(icon: Icon(Icons.person)),
Tab(icon: Icon(Icons.email)),
],
);
}
}
需要TabController
来协调TabBar
和TabBarView
之间的选项卡选择。TabController
构造函数的长度参数是选项卡总数。每当框架触发状态更改时,需要TickerProvider
来触发通知。TickerProvider
是垂直同步的。每当您创建新的TabController
时,将vsync:this
传递给TabController
构造函数。
TickerProvider
是由可以实现Ticker
对象类的接口。每当帧触发时必须同追任何对象都可以使用Tickers
,但它们最常通过AnimationController
间接使用。AnimationContrllers
需要一个TicjerProvider
来获取它们Ticker
。如果您从State
创建AnimationCoontrooller
,则可以使用高TickerProviderStateMixin
或SingleTickerProviderStateMixin
类来获取合适的TickerProvider
。
Scaffold
小部件包装了一个新的TabBar
小部件并创建了两个选项卡。TabBarView
小部件作为Scaffold
小部件的body
参数传递。与TabBar
小部件的选项卡对应的所有屏幕都是TabBarView
小部件的子项以及相同的TabController
。
import 'package:flutter/material.dart';
class _NavigationHomePageState extends State<NavigationHomePage> with SingleTickerProviderStateMixin {
late TabController controller = TabController(length: 2, vsync: this);
@override
Widget build(BuildContext context) {
return Scaffold(
bottomNavigationBar: const Material(
color: Colors.blue,
child: TabBar(
tabs: <Tab>[
Tab(icon: Icon(Icons.person),),
Tab(icon: Icon(Icons.email),),
],
),
),
body: TabBarView(
controller: controller,
children: const <Widget>[
HomeScreen(),
TabScreen(),
],
),
);
}
}
3.2.2、抽屉导航
在React Native中,导入所需的react-navigation包,然后使用createDrawerNavigator
和DrawerNavigation
。
// React Native
export default (MyApp1 = DrawerNavigator({
Home: {
screen: SimpleApp
},
Screen2: {
screen: drawerScreen
}
}));
在Flutter中,我们可以结合Drawer
小部件和Scaffold
来创建带有Material Design抽屉的布局。要将抽屉添加到应用程序,请将其包装在Scaffold
小部件中。Scaffold
小部件为遵循Material Design准则的应用程序提供一致的视觉结构。它还支持特殊的Material Design组件,例如Drawers
、AppBars
和SnackBars
。
Drawer
小部件是一个Material Design面板,它从Scaffold
的边缘水平滑入以显示应用程序中的导航链接。您可以提供一个ElevatedButton
、Text
小部件或项目列表以显示为Drawer
小部件的子项。在以下示例中,ListTile
小部件提供点击导航。
@override
Widget build(BuildContext context) {
return Drawer(
elevation: 2.0,
child: ListTile(
leading: const Icon(Icons.change_history),
title: const Text('Screen2'),
onTap: () {
Navigator.of(context).pushNamed('/b');
},
),
);
}
该Scaffold
小部件还包括一个AppBar
小部件,当Scaffold
中有可用的Drawer
时,它会自动显示适当的IconButton以显示抽屉。Scaffold
自动处理边缘滑动手势以显示Drawer
。
@override
Widget build(BuildContext context) {
return Scaffold(
drawer: Drawer(
elevation: 2.0,
child: ListTile(
leading: const Icon(Icons.change_history),
title: const Text('Screen2'),
onTap: () {
Navigator.of(context).pushNamed('/b');
},
),
),
appBar: AppBar(title: const Text('Home'),),
body: Container(),
);
}
- Android
- iOS
四、手势监测和触摸时间处理
为了监听和相应手势,Flutter支持点击、拖动和缩放。Flutter中的手势系统有两个独立层,第一层包括原始指针时候ii教案,它描述指针在屏幕的位置和移动(例如触摸、鼠标和手写笔移动)。第二层包括手势,它描述了由一个或多个指针移动组成的语义动作。
4.1、如何向小部件添加点击或按下监听器?
在React Native中,使用PanResponder
或组件将监听器添加到Touchable
组件。
// React Native
<TouchableOpacity
onPress={() => {
console.log('Press');
}}
onLongPress={() ={
console.log('Long Press')
}}
>
<Text>Tap or Long Press</Text>
</TouchableOpacity>
对于更复杂的手势和将多个触摸组合成一个手势,PanResponder
使用。
// React Native
class App extends Component {
componentWillMount() {
this._panResponder = PanResponder.create({
onMoveShouldSetPanResponder: (event, gestureState) => !!getDirection(gestureState),
onPanResponderMove: (event, gestureState) => true,
onPanResponderRelease: (event, gestureState) => {
const drag = getDirection(gestureState);
},
onPanSponderTerminationRequest: (event, gestureState) => true
});
}
render() {
return (
<View style={styles.containre} {...this._panResponder.panHanders}>
<View style={styles.center}>
<Text>Swipe Horizontally or Verucally</Text>
</View>
</View.
);
}
}
在Flutter中,要向小部件添加点击(或按下)监听器,请使用按钮或具有onPress: field
。或者,通过将其包装在GestureDetector
.
@override
Widget build(BuildContext context) {
return GestureDetector(
child: Scaffold(
appBar: AppBar(title: const Text('Gestures'),),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: const <Widget>[
Text('Tap, Long Press, Swipe Horizontally o Vertically')
],
),
),
),
onTap: () {
print('Tapped');
},
onLongPress: () {
print('Long Pressed');
},
onVerticalDragEnd: (value) {
print('Swiped Vertically');
},
onHorizontalDragEnd: (value) {
print('Swiped Horizontally');
},
);
}
有关更多信息,包括 FlutterGestureDetector
回调列表,请参阅GestureDetector 类。
- Android
- iOS
五、发出HTTP网络请求
从Internet获取数据大多数应用程序来说很常见。在Flutter中,http
包提供了从互联网获取数据的最简单方法。
5.1、如何从API调用获取数据?
React Native为网络提供了Fetch API——您发出一个获取请求,然后接收相应以获取数据。
// React Native
_getIpAddress = () => {
fetch('https://httpbin.org/ip')
.then(response => response.json())
.then(responseJson => {
this.setState({ _ipAddress: responseJson.origin });
})
.catch(error => {
console.error(error);
});
};
Flutter使用http
包。要安装http
包,请将其添加到pubspec.yaml的依赖项部分。
dependencies:
flutter:
sdk: flutter
http: <latest_version>
Flutter使用dart.io
核心HTTP支持客户端。要创建HTTP客户端,请导入dart:io
.
import 'dart:io';
客户端支持以下HTTP操作:GET、POST、PUT和DELETE。
final url = Uri.parse('https://httpbin.org/ip');
final httpClient = HttpClient();
Future<void> getIPAddress() async {
final request = await httpClient.getUrl(url);
final response = await request.close();
final responseBody = await response.transform(utf8.devoder).join();
final String ip = jsonDecode(responseBody)['origin'];
setState(() {
_ipAddress = ip;
});
}
- Android
- iOS
六、表单输入
文本字段允许用户在您的应用程序中键入文本,以便它们可用于构造表单、消息传递应用程序、消息传递应用程序、搜索体验等。Flutter提供了两个核心的文本字段小部件:TextField
和TextFormField
.
6.1、如何使用文本字段小部件?
在React Native中,要输入文本,您可以使用TextInput
组件来显示文本输入框,然后使用回调将值存储在变量中。
// React Native
<TextInput
placeholder="Enter your Password"
onChangeText={password => this.setState({ password })}
/>
<Button title="Submit" onPress={this.validata} />
在Flutter中,使用TextEditingController
类来管理TextField
小部件。每当修改文本字段时,控制器都会通知其监听器。
监听器读取文本和选择属性以及了解用户在字段中键入的内容过。您可以通过控制器TextField
的属性访问文本.
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
final TextEditingController _controller = TextEditingController();
class AA extends StatelessWidget {
const AA({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Column(
children: [
TextField(
controller: _controller,
decoration: const InputDecoration(
hintText: 'Type something',
labelText: 'Text Field'
),
),
ElevatedButton(
onPressed: () {
showDialog(context: context, builder: (context) {
return AlertDialog(
title: const Text('Alert'),
content: Text('You typed ${_controller.text}'),
)
});
},
child: const Text('Submit'),
)
],
);
}
}
在此示例中,当用户点击提交按钮时,一个金高对话框会显示在文本字段中输入的当前文本。这是使用AlertDialog
显示警报消息的小部件实现的,TextField
中的文本由TextEditingController
的文本属性访问。
6.2、如何使用表单小部件
在Flutter中,使用Form
小部件,其中TextFormField
小部件和提交按钮作为子项传递。该TextFormField
小部件有一个名为onSaved
的参数。它接受回调并在保存表单时执行。FormState
对象用于保存、重置或验证作为此Form
子项的每个FormField
,您可以将Form.of()
与其父类为Form
的上下文一起使用,或者将GlobalKey
传递给Form
构造函数并调用GlobalKey.currentState()
。
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
final TextEditingController _controller = TextEditingController();
class AA extends StatelessWidget {
const AA({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Form(
key: formKey,
child: Column(
children: <Widget>[
TextFormField(
validator: (value) {
if (value != null && value.contains('@')) {
return null;
}
return 'Not a valid email';
},
onSaved: (val) {
_email = val;
},
decoration: const InputDecoration(
hintText: 'Enter your email',
labelText: 'Eamil',
),
),
ElevatedButton(
onPressed: _submit,
child: const Text('Login'),
)
],
),
);
}
}
以下示例显示如何使用Form.save()
和formKey
(这是一个GlobalKey
)在提交时保存表单。
void _submit() {
final form = formKey.currentState;
if (form != null && form.validate()) {
form.save();
showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: const Text('Alert'),
content: Text('Email: $_email, password: $_password')
);
}
);
}
}
- Android
- iOS
转载自:https://juejin.cn/post/7186606142175313978