阅读 70

Flutter学习笔记2,Dart基础-函数

1,函数的定义

返回值 函数的名称(参数列表) {
  函数体
  return 返回值
}
复制代码

按照上面的格式定义一个简单的计算两个数字相加:

num sum(num a, num b) {
  return a + b;
}
复制代码

函数的参数和返回值类型也可以省略,这样这个函数可以计算所有支持+运算的处理,并自动推导对应的类型:

  print(sum("abc", "def").runtimeType); // String
  print(sum(1, 2).runtimeType); // int
  print(sum(1, 2.0).runtimeType); // double
  print(sum(1.0, 2.0).runtimeType); // double
  print(sum([1, 2, 3], [4, 5, 6]).runtimeType); // List<int>

sum(a, b) {
  return a + b;
}
复制代码

但函数体中,只有一行表达式的时候,可以使用箭头语法,上面的sum函数可以写成:

sum(a, b) => a + b;
复制代码

所有的函数的都有返回值,如果没有返回值,默认返回null。

2,可选参数

实现可选参数,可以使用两种方式:

命名可选参数: {param1, param2, ...}
位置可选参数: [param1, param2, ...]
复制代码

1,命名可选参数

printInfo1("Jack", height: 1.88, age: 18);

void printInfo1(String name, {int age, double height}) {
  print("name: $name, age: $age, height: $height");
}
复制代码

命名可选参数将可选参数统一用{}扩起来,定义在必传参数后面,当函数调用时,先传入必传参数,再通过 别名: 参数值 的格式传入参数。因为通过参数别名来传递,所以命名可选参数不需要按照函数声明的顺序传递。 Flutter中的组件都是通过这种命名可选参数进行传值,例如:

  Widget build(BuildContext context) {
    return MaterialApp(
      home: HomePage(),
      debugShowCheckedModeBanner: false,
    );
  }
  
    Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: HomePage(),
    );
  }
复制代码

MaterialApp中的初始化方法中,home和debugShowCheckedModeBanner参数的传入顺序可以变化,通过查看源码可以看到,所有的参数都是使用{}扩起来,定义为命名可选参数:

const MaterialApp({
    Key key,
    this.navigatorKey,
    this.home,
    this.routes = const <String, WidgetBuilder>{},
    this.initialRoute,
    ...
  }) 
复制代码

在Flutter开发环境中,还可以对命名可选参数添加@required关键字,让对应的命名可选参数变成必传参数:

void printInfo1(
  String name, 
  {
    @required int age,
    double height,
  }) {
  print("name: $name, age: $age, height: $height");
}
复制代码

之所以这样做,因为dart中,默认的必传参数是没有别名并且必须按照声明顺序传递,而通过命名可选参数加@required关键字,就可以实现带参数别名、不限制顺序并且必传的需求。Flutter中大量采用这种参数的声明方式,比如RaisedButton的初始化方法中,onPressed参数的声明:

  const RaisedButton({
    Key key,
    @required VoidCallback onPressed,
    VoidCallback onLongPress,
    ValueChanged<bool> onHighlightChanged,
    ButtonTextTheme textTheme,
    ...
  }) 
复制代码

2,位置可选参数

位置可选参数在传入参数时,没有参数别名,必须按照对应的位置来传递:

void printInfo2(String name, [int age, double height]) {
  print('name=$name age=$age height=$height');
}
复制代码

3,参数默认值

只有可选参数才可以设置默认值:

void printInfo1(String name, {int age = 0, double height = 1.7}) {
  print('name=$name age=$age height=$height');
}
复制代码

3,函数是一等公民

在iOS开发中,Objective-C不同于Swift中的一点,就是它不支持将函数作为一等公民来使用,函数不能作为变量和参数、和返回值使用。Dart支持这一特性。

1,函数赋值给变量

  var sunFunc = sum;
  print(sunFunc(1, 2));
  
 sum(a, b) => a + b;
复制代码

2,函数作为参数

如下格式,创建一个函数,传入两个int类型值,和一个传入两个int类型参数返回一个int类型的函数。在函数体中,调用传入的函数,并前两个参数传入,并返回最终值:

int test(int a, int b, int f(int e1, int e2)) {
  return f(a, b);
} 
复制代码

下面可以将函数做为参数传入,当函数作为参数时,只需要写函数名:

  print(test(3, 4, sum)); // 7
  print(test(5, 3, sub)); // 2

int sum(int a, int b) => a + b;
int sub(int a, int b) => a - b;

int test(int a, int b, int f(int e1, int e2)) {
  return f(a, b);
} 
复制代码

3,匿名函数

还是上面定义的函数:

int test(int a, int b, int f(int e1, int e2)) {
  return f(a, b);
} 
复制代码

上面都是通过传入一个定义好的函数的函数名来实现,下面可以通过匿名函数的方式来实现:

  print(test(2, 3, (a, b){
    return a * b;
  }));  // 6
复制代码

如果要传入的函数只有一个表达式,可以用箭头表达式简写:

print(test(2, 3, (a, b) => a * b)); // 6
复制代码

匿名函数在Flutter开发中,很多地方都有用到,比如List的遍历:

List<int> list = [1, 2, 3];
list.forEach((element) => print(element));
复制代码

或者Flutter开发中,RaisedButton的onPressed:

        RaisedButton(
          child: Text("+", style: TextStyle(fontSize: 20, color: Colors.white),),
          color: Colors.pink,
          onPressed: (){
            print("+ pressed");
          },
        ),
复制代码

4,词法闭包

闭包可以访问其词法范围内的变量,即使函数在其他地方被使用,也可以正常的访问。

main(List<String> args) {
  makeAdder(num addBy) {
    return (num i) {
      return i + addBy;
    };
  }

  var adder2 = makeAdder(2);
  print(adder2(10)); // 12
  print(adder2(6)); // 8

  var adder5 = makeAdder(5);
  print(adder5(10)); // 15
  print(adder5(6)); // 11
}
复制代码