提取的小部件内的 Flutter Slider

Flutter Slider inside a Extracted Widget

好的,我想说得更具体一些。我的目标是将我的部分代码 细分为单独的小部件 以便主树尽可能保持干净。

在下面的示例中,我在 StatefulWidget 中有一个 主树。我已经成功地将带有按钮 的容器转换成它自己的带有Extract to Widget 的小部件。按钮的 setState 通过 constructor.

起作用

现在我想对第二个装有滑块的容器做同样的事情。但是我不知道如何使用构造函数 处理 Slider onChanged setState,因为 setState 在提取的 Widget 中不起作用。

这是代码示例,工作按钮作为提取的小部件,滑块仍在主树中。

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: MyStfl(),
    );
  }
}

class MyStfl extends StatefulWidget {
  @override
  _MyStflState createState() => _MyStflState();
}

class _MyStflState extends State<MyStfl> {
  double sliderPos = 0.0;
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Container(
        decoration: BoxDecoration(
          color: Color(0x80061A28),
        ),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: [
            Expanded(
              child: MyCardA(
                aktion: () {
                  setState(() {
                    print('Button Pressed');
                  });
                },
              ),
            ),
            Expanded(
              child: Container(
                child: Slider(
                  divisions: 48,
                  value: sliderPos,
                  min: 0.0,
                  max: 24.0,
                  onChanged: (double newValue) {
                    setState(
                      () {
                        sliderPos = double.parse(newValue.toStringAsFixed(1));
                        print(sliderPos);
                      },
                    );
                  },
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

class MyCardA extends StatelessWidget {
  MyCardA({this.aktion});
  final Function aktion;

  @override
  Widget build(BuildContext context) {
    return Container(
      margin: EdgeInsets.all(20.0),
      padding: EdgeInsets.all(20.0),
      color: Color(0x80125889),
      child: GestureDetector(
        onTap: aktion,
        child: Text(
          'MY BUTTON',
          style: TextStyle(
            color: Color(0xFF3D3939),
            fontSize: 22.0,
          ),
        ),
      ),
    );
  }
}

首先,您要在 CardA 组件中更改一件事。不要将 aktion 参数键入 Function(这非常通用),您应该给它一个类型 void Function(),说:它是一个 return 什么都没有的函数(空白)。这就是 GestureDetectoronTap 参数所期望的。传递任何其他类型的函数不是您想要做的事情,因为它会破坏您的应用程序。键入参数时尽可能具体。您可以将此 void Function() 类型缩短为 VoidCallback。都是一样的。

我也做了参数required。这不是必需的,但它是你想要的(我认为)。如果您希望能够发出此参数,请删除 required 关键字并将您的 aktion 键入 void Function()?? 表示您可以传递 void Function()null.

class MyCardA extends StatelessWidget {
  MyCardA({required this.aktion});
  final void Function() aktion;

  @override
  Widget build(BuildContext context) {
    return Container(
      margin: EdgeInsets.all(20.0),
      padding: EdgeInsets.all(20.0),
      color: Color(0x80125889),
      child: GestureDetector(
        onTap: aktion,
        child: Text(
          'MY BUTTON',
          style: TextStyle(
            color: Color(0xFF3D3939),
            fontSize: 22.0,
          ),
        ),
      ),
    );
  }
}

对于'MyCardB',您需要设置一个带有参数的回调函数。有点复杂。

一个Slider组件有一个onChanged参数,当滑块位置改变时触发。这为您提供了一个 double(新位置)并允许您使用它做一些事情。这被键入为 void Function(double)。我们有一个函数获取一个 double 作为参数,return 什么都没有 (void).

您可以在下面的自定义组件中看到它的外观。

class MyCardB extends StatelessWidget {
  const MyCardB({
    required this.sliderPos,
    required this.onChanged,
  });

  final double sliderPos;
  final void Function(double) onChanged;

  @override
  Widget build(BuildContext context) {
    return Container(
      child: Slider(
        divisions: 48,
        value: sliderPos,
        min: 0.0,
        max: 24.0,
        onChanged: onChanged,
      ),
    );
  }
}

然后您在主组件中使用这个新组件,如下所示。

Expanded(
  child: MyCardB(
    sliderPos: sliderPos,
    onChanged: (double newValue) {
      setState(
        () {
          sliderPos = double.parse(newValue.toStringAsFixed(1));
        },
      );
    },
  ),
),

长话短说,您想利用 callback functions。如果您不熟悉它们,我强烈建议您仔细阅读它们,它们非常重要。正如您正确注意到的那样,您无法执行 child 小部件内的逻辑;您想将其保留在 parent 中。您可以通过创建这些回调函数来管理它。

您在 parent 中创建回调函数。 您将函数传递给 child 小部件。 一旦触发,将在 parent 内执行一个函数。 child 只知道它必须触发函数,但所有的逻辑都对它隐藏了。