从另一个有状态的小部件调用有状态小部件的方法

huangapple go评论115阅读模式
英文:

Calling a stateful widget's method from another stateful widget

问题

I can help you with the translation. Here is the relevant part of your text:

"我是Flutter新手,最近遇到了一个挑战。我有以下文件:

  1. main.dart
import 'package:chatgpt_chat/testpage.dart';
import 'package:flutter/material.dart';

void main() => runApp(const TestAPP());

class TestAPP extends StatelessWidget {
  const TestAPP({super.key});

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: TestScaffold(),
    );
  }
}

class TestScaffold extends StatefulWidget {
  const TestScaffold({super.key});

  @override
  State<TestScaffold> createState() => _TestScaffoldState();
}

class _TestScaffoldState extends State<TestScaffold> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Timer Example'),
      ),
      body: const Center(
        child: TimerWidget(),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: (){},
      ),
    );
  }
}
  1. timerWidget.dart:
import 'dart:async';

import 'package:flutter/material.dart';

class TimerWidget extends StatefulWidget {
  const TimerWidget({Key? key}) : super(key: key);

  @override
  State<TimerWidget> createState() => _TimerWidgetState();
}

class _TimerWidgetState extends State<TimerWidget> {
  int _seconds = 0;
  late Timer _timer;

  @override
  void initState() {
    super.initState();
    _startTimer();
  }

  void _startTimer() {
    _timer = Timer.periodic(const Duration(seconds: 1), (timer) {
      setState(() {
        _seconds++;
      });
    });
  }

  void _stopTimer() {
    _timer.cancel();
  }

  void _resetTimer() {
    setState(() {
      _seconds = 0;
    });
  }

  @override
  void dispose() {
    _stopTimer();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    String formattedTime = _formatTime(_seconds);

    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        Text(
          formattedTime,
          style: const TextStyle(fontSize: 24),
        ),
        const SizedBox(height: 16),
        Row(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            ElevatedButton(
              onPressed: _stopTimer,
              child: const Text('Stop'),
            ),
            const SizedBox(width: 16),
            ElevatedButton(
              onPressed: _resetTimer,
              child: const Text('Reset'),
            ),
          ],
        ),
      ],
    );
  }

  String _formatTime(int seconds) {
    int minutes = seconds ~/ 60;
    int remainingSeconds = seconds % 60;
    return '$minutes:${remainingSeconds.toString().padLeft(2, '0')}';
  }
}

Could you please help me out by showing how I can call the startTimer method from TimerWidget in FloatingActionButton? I have a problem in a different program, and solving this particular problem will help me out. I really appreciate it if you provide your solutions.

I looked for similar posts; however, they do not fully solve my problem. I just have to use the startTimer method in the FloatingActionButton."

英文:

I'm new in Flutter, and recently I have encountered with a challange. I have the following files:

  1. main.dart
import &#39;package:chatgpt_chat/testpage.dart&#39;;
import &#39;package:flutter/material.dart&#39;;
void main() =&gt; runApp(const TestAPP());
class TestAPP extends StatelessWidget {
const TestAPP({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
home: TestScaffold(),
);
}
}
class TestScaffold extends StatefulWidget {
const TestScaffold({super.key});
@override
State&lt;TestScaffold&gt; createState() =&gt; _TestScaffoldState();
}
class _TestScaffoldState extends State&lt;TestScaffold&gt; {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text(&#39;Timer Example&#39;),
),
body: const Center(
child: TimerWidget(),
),
floatingActionButton: FloatingActionButton(onPressed: (){}),
);
}
}
  1. timerWidget.dart:
import &#39;dart:async&#39;;
import &#39;package:flutter/material.dart&#39;;
class TimerWidget extends StatefulWidget {
const TimerWidget({Key? key}) : super(key: key);
@override
State&lt;TimerWidget&gt; createState() =&gt; _TimerWidgetState();
}
class _TimerWidgetState extends State&lt;TimerWidget&gt; {
int _seconds = 0;
late Timer _timer;
@override
void initState() {
super.initState();
_startTimer();
}
void _startTimer() {
_timer = Timer.periodic(const Duration(seconds: 1), (timer) {
setState(() {
_seconds++;
});
});
}
void _stopTimer() {
_timer.cancel();
}
void _resetTimer() {
setState(() {
_seconds = 0;
});
}
@override
void dispose() {
_stopTimer();
super.dispose();
}
@override
Widget build(BuildContext context) {
String formattedTime = _formatTime(_seconds);
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
formattedTime,
style: const TextStyle(fontSize: 24),
),
const SizedBox(height: 16),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: _stopTimer,
child: const Text(&#39;Stop&#39;),
),
const SizedBox(width: 16),
ElevatedButton(
onPressed: _resetTimer,
child: const Text(&#39;Reset&#39;),
),
],
),
],
);
}
String _formatTime(int seconds) {
int minutes = seconds ~/ 60;
int remainingSeconds = seconds % 60;
return &#39;$minutes:${remainingSeconds.toString().padLeft(2, &#39;0&#39;)}&#39;;
}
}

Could you please help me out by showing how I can call startTimer method from TimerWidget in FloatingActionButton? I have a problem in different program, and solving this particular problem will help me out. I really appreciate it if you provide your solutions.

I looked for similar posts, however they do not fully solve my problem. I just have to use startTimer method in floatingActionButton

答案1

得分: 2

以下是您要翻译的代码部分:

您可以创建一个自定义的 `TimerWidgetController`,并将其作为参数传递给 `TimerWidget`,以便在构建时将函数 `_startTime` 的引用分配给它。

通过这样做,您无需将任何内容公开在 `TimerWidget` 中。

现在,可以通过使用 `TimerWidgetController` 的实例来从其他小部件调用该函数,当然要使用与 `TimeWidget` 相同的实例。

请检查代码:

 - timer_widget_controller.dart

```dart

class TimerWidgetController {
  late Function? startTimer;
}

  • timer_widget.dart
import 'dart:async';

import 'package:flutter/material.dart';

import 'timer_widget_controller.dart';

class TimerWidget extends StatefulWidget {
  final TimerWidgetController controller;
  const TimerWidget({
    Key? key,
    required this.controller,
  }) : super(key: key);

  @override
  State<TimerWidget> createState() => _TimerWidgetState();
}

class _TimerWidgetState extends State<TimerWidget> {
  int _seconds = 0;
  late Timer _timer;

  @override
  void initState() {
    super.initState();
    widget.controller.startTimer = _startTimer;
    _startTimer();
  }

  void _startTimer() {
    _timer = Timer.periodic(const Duration(seconds: 1), (timer) {
      setState(() {
        _seconds++;
      });
    });
  }

  void _stopTimer() {
    _timer.cancel();
  }

  void _resetTimer() {
    setState(() {
      _seconds = 0;
    });
  }

  @override
  void dispose() {
    _stopTimer();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    String formattedTime = _formatTime(_seconds);

    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        Text(
          formattedTime,
          style: const TextStyle(fontSize: 24),
        ),
        const SizedBox(height: 16),
        Row(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            ElevatedButton(
              onPressed: _stopTimer,
              child: const Text('Stop'),
            ),
            const SizedBox(width: 16),
            ElevatedButton(
              onPressed: _resetTimer,
              child: const Text('Reset'),
            ),
          ],
        ),
      ],
    );
  }

  String _formatTime(int seconds) {
    int minutes = seconds ~/ 60;
    int remainingSeconds = seconds % 60;
    return '$minutes:${remainingSeconds.toString().padLeft(2, '0')}';
  }
}

  • main.dart
import 'package:flutter/material.dart';

import 'timer_widget_controller.dart';
import 'timer_widget.dart';

void main() => runApp(const TestAPP());

class TestAPP extends StatelessWidget {
  const TestAPP({super.key});

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: TestScaffold(),
    );
  }
}

class TestScaffold extends StatefulWidget {
  const TestScaffold({super.key});

  @override
  State<TestScaffold> createState() => _TestScaffoldState();
}

class _TestScaffoldState extends State<TestScaffold> {
  late final TimerWidgetController _timerWidgetController;

  @override
  void initState() {
    super.initState();
    _timerWidgetController = TimerWidgetController();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Timer Example'),
      ),
      body: Center(
        child: TimerWidget(
          controller: _timerWidgetController,
        ),
      ),
      floatingActionButton: FloatingActionButton(onPressed: () {
        _timerWidgetController.startTimer?.call();
      }),
    );
  }
}

希望这有助于您理解代码的内容。

英文:

You could create a custom TimerWidgetController that TimerWidget requires as parameter, when built, in order to assign the reference of the function _startTime to it.

By doing this, you don't need to make anything public in the TimerWidget.

The function can now be called from other widgets just by using an instance of TimerWidgetController, the same one passed to TimeWidget of course.

Please check the code:

  • timer_widget_controller.dart

class TimerWidgetController {
late Function? startTimer;
}
  • timer_widget.dart
import &#39;dart:async&#39;;
import &#39;package:flutter/material.dart&#39;;
import &#39;timer_widget_controller.dart&#39;;
class TimerWidget extends StatefulWidget {
final TimerWidgetController controller;
const TimerWidget({
Key? key,
required this.controller,
}) : super(key: key);
@override
State&lt;TimerWidget&gt; createState() =&gt; _TimerWidgetState();
}
class _TimerWidgetState extends State&lt;TimerWidget&gt; {
int _seconds = 0;
late Timer _timer;
@override
void initState() {
super.initState();
widget.controller.startTimer = _startTimer;
_startTimer();
}
void _startTimer() {
_timer = Timer.periodic(const Duration(seconds: 1), (timer) {
setState(() {
_seconds++;
});
});
}
void _stopTimer() {
_timer.cancel();
}
void _resetTimer() {
setState(() {
_seconds = 0;
});
}
@override
void dispose() {
_stopTimer();
super.dispose();
}
@override
Widget build(BuildContext context) {
String formattedTime = _formatTime(_seconds);
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
formattedTime,
style: const TextStyle(fontSize: 24),
),
const SizedBox(height: 16),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: _stopTimer,
child: const Text(&#39;Stop&#39;),
),
const SizedBox(width: 16),
ElevatedButton(
onPressed: _resetTimer,
child: const Text(&#39;Reset&#39;),
),
],
),
],
);
}
String _formatTime(int seconds) {
int minutes = seconds ~/ 60;
int remainingSeconds = seconds % 60;
return &#39;$minutes:${remainingSeconds.toString().padLeft(2, &#39;0&#39;)}&#39;;
}
}
  • main.dart
import &#39;package:flutter/material.dart&#39;;
import &#39;timer_widget_controller.dart&#39;;
import &#39;timer_widget.dart&#39;;
void main() =&gt; runApp(const TestAPP());
class TestAPP extends StatelessWidget {
const TestAPP({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
home: TestScaffold(),
);
}
}
class TestScaffold extends StatefulWidget {
const TestScaffold({super.key});
@override
State&lt;TestScaffold&gt; createState() =&gt; _TestScaffoldState();
}
class _TestScaffoldState extends State&lt;TestScaffold&gt; {
late final TimerWidgetController _timerWidgetController;
@override
void initState() {
super.initState();
_timerWidgetController = TimerWidgetController();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text(&#39;Timer Example&#39;),
),
body: Center(
child: TimerWidget(
controller: _timerWidgetController,
),
),
floatingActionButton: FloatingActionButton(onPressed: () {
_timerWidgetController.startTimer?.call();
}),
);
}
}

答案2

得分: 1

将您的 _TestScaffoldState 更改为

class _TestScaffoldState extends State<TestScaffold> {
  GlobalKey<TimerWidgetState> key = GlobalKey<TimerWidgetState>();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Timer Example'),
      ),
      body: Center(
        child: TimerWidget(key: key),
      ),
      floatingActionButton: FloatingActionButton(onPressed: (){
        key.currentState?.startTimer();
      }),
    );
  }
}

此外,您需要将 startTimerTimerWidgetState 更改为公开,去掉下划线以便可以使用。如下所示:

class TimerWidget extends StatefulWidget {
  const TimerWidget({Key? key}) : super(key: key);

  @override
  State<TimerWidget> createState() => TimerWidgetState();
}

class TimerWidgetState extends State<TimerWidget> {
  int _seconds = 0;
  late Timer _timer;

  @override
  void initState() {
    super.initState();
    startTimer();
  }

  void startTimer() {
  ...
英文:

Change your _TestScaffoldStateto

class _TestScaffoldState extends State&lt;TestScaffold&gt; {
GlobalKey&lt;TimerWidgetState&gt; key = GlobalKey&lt;TimerWidgetState&gt;();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text(&#39;Timer Example&#39;),
),
body: Center(
child: TimerWidget(key: key),
),
floatingActionButton: FloatingActionButton(onPressed: (){
key.currentState?.startTimer();
}),
);
}
}

Furthermore you need to make startTimer and TimerWidgetState public by removing the underscore so they can be used. So like:

class TimerWidget extends StatefulWidget {
const TimerWidget({Key? key}) : super(key: key);
@override
State&lt;TimerWidget&gt; createState() =&gt; TimerWidgetState();
}
class TimerWidgetState extends State&lt;TimerWidget&gt; {
int _seconds = 0;
late Timer _timer;
@override
void initState() {
super.initState();
startTimer();
}
void startTimer() {
...

答案3

得分: 0

我不知道为什么你要以这种困难的方式运行计时器(调用startTimer方法),但我现在会按照你的要求来做。

因为其他人已经提到了如何使用GlobalKey以及如何创建和解决控制器的方法,我将展示一种更加基本的方式。

我将创建一个简单的示例代码并展示给你。

-main.dart

import 'dart:async';
import 'package:flutter/material.dart';

class TestScaffold extends StatefulWidget {
  const TestScaffold({
    Key? key,
  }) : super(key: key);

  @override
  State<TestScaffold> createState() => _TestScaffoldState();
}

class _TestScaffoldState extends State<TestScaffold> {
  bool timer = false;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Timer Example'),
      ),
      body: Center(child: TimerWidget(timerBool: timer)),
      floatingActionButton: FloatingActionButton(
        onPressed: _callTimer,
        child: const Icon(Icons.play_arrow_rounded),
      ),
    );
  }

  void _callTimer() {
    setState(() {
      timer = true;
    });
  }
}

-timerWidget.dart

class TimerWidget extends StatefulWidget {
  bool timerBool;

  TimerWidget({
    Key? key,
    required this.timerBool,
  }) : super(key: key);

  @override
  State<TimerWidget> createState() => _TimerWidgetState();
}

class _TimerWidgetState extends State<TimerWidget> {
  int _seconds = 0;
  Timer _timer = Timer(const Duration(seconds: 1), () {});
  String timerCheck = 'ready';

  @override
  void initState() {
    super.initState();
  }

  void _startTimer() {
    timerCheck = 'startinggg';
    _timer = Timer.periodic(const Duration(seconds: 1), (timer) {
      setState(() {
        _seconds++;
      });
    });
    print('startTimer ${_timer.hashCode}');
  }

  @override
  Widget build(BuildContext context) {
    String formattedTime = _formatTime(_seconds);
    if (widget.timerBool && !_timer.isActive) {
      _startTimer();
    }

    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        Text(
          timerCheck,
          style: Theme.of(context).textTheme.headline6,
        ),
        const SizedBox(height: 20),
        Text(
          formattedTime,
          style: const TextStyle(fontSize: 24),
        ),
        const SizedBox(height: 16),
        Row(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            ElevatedButton(
              onPressed: _stopTimer,
              child: const Text('Stop'),
            ),
            const SizedBox(width: 16),
            ElevatedButton(
              onPressed: _resetTimer,
              child: const Text('Reset'),
            ),
          ],
        ),
      ],
    );
  }

  void _stopTimer() {
    if (!widget.timerBool) return;
    setState(() {
      timerCheck = 'stopppp';
      widget.timerBool = false;
    });
    _timer.cancel();
    print('cancel ${_timer.hashCode}');
  }

  void _resetTimer() {
    setState(() {
      _seconds = 0;
    });
  }

  String _formatTime(int seconds) {
    int minutes = seconds ~/ 60;
    int remainingSeconds = seconds % 60;
    return '$minutes:${remainingSeconds.toString().padLeft(2, '0')}';
  }

  @override
  void dispose() {
    _stopTimer();
    super.dispose();
  }
}

顺便提一下,你之前提到的一个问题是,连续按下FloatingActionButton会导致多个计时器,只有最后一个生成的计时器可以被取消。因此,你需要在startTimer()中添加一个条件,如下所示:

if (_timer.isActive) return;

这个条件可以确保只有在当前计时器不活动时才启动新的计时器。

英文:

I don't know why you're trying to run the timer(call startTimer method) in a difficult way, but I'll do what you want for now.

Since other people mentioned above how to use GlobalKey and how to create and solve a controller, I'll make it a more primitive way.

I'll make a simple example code and show it to you.

-main.dart

import &#39;dart:async&#39;;
import &#39;package:flutter/material.dart&#39;;
class TestScaffold extends StatefulWidget {
const TestScaffold({
Key? key,
}) : super(key: key);
@override
State&lt;TestScaffold&gt; createState() =&gt; _TestScaffoldState();
}
class _TestScaffoldState extends State&lt;TestScaffold&gt; {
bool timer = false;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(&#39;Timer Example&#39;),
),
body: Center(child: TimerWidget(timerBool: timer)),
floatingActionButton: FloatingActionButton(
onPressed: _callTimer,
child: const Icon(Icons.play_arrow_rounded),
),
);
}
void _callTimer() {
setState(() {
timer = true;
});
}
}

-timerWidget.dart

class TimerWidget extends StatefulWidget {
bool timerBool;
TimerWidget({
Key? key,
required this.timerBool,
}) : super(key: key);
@override
State&lt;TimerWidget&gt; createState() =&gt; _TimerWidgetState();
}
class _TimerWidgetState extends State&lt;TimerWidget&gt; {
int _seconds = 0;
Timer _timer = Timer(const Duration(seconds: 1), () {});
String timerCheck = &#39;ready&#39;;
@override
void initState() {
super.initState();
}
void _startTimer() {
timerCheck = &#39;startinggg&#39;;
_timer = Timer.periodic(const Duration(seconds: 1), (timer) {
setState(() {
_seconds++;
});
});
print(&#39;startTimer ${_timer.hashCode}&#39;);
}
@override
Widget build(BuildContext context) {
String formattedTime = _formatTime(_seconds);
if (widget.timerBool &amp;&amp; !_timer.isActive) {
_startTimer();
}
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
timerCheck,
style: Theme.of(context).textTheme.headlineMedium,
),
const SizedBox(height: 20),
Text(
formattedTime,
style: const TextStyle(fontSize: 24),
),
const SizedBox(height: 16),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: _stopTimer,
child: const Text(&#39;Stop&#39;),
),
const SizedBox(width: 16),
ElevatedButton(
onPressed: _resetTimer,
child: const Text(&#39;Reset&#39;),
),
],
),
],
);
}
void _stopTimer() {
if (!widget.timerBool) return;
setState(() {
timerCheck = &#39;stopppp&#39;;
widget.timerBool = false;
});
_timer.cancel();
print(&#39;cancel ${_timer.hashCode}&#39;);
}
void _resetTimer() {
setState(() {
_seconds = 0;
});
}
String _formatTime(int seconds) {
int minutes = seconds ~/ 60;
int remainingSeconds = seconds % 60;
return &#39;$minutes:${remainingSeconds.toString().padLeft(2, &#39;0&#39;)}&#39;;
}
@override
void dispose() {
_stopTimer();
super.dispose();
}
}

For your information, there is a part that the two of you who answered above missed.
Pressing FloatingActionButton continuously results in multiple timers,
and only the last generated one can be canceled().

Therefore, you need to put one condition in startTimer() as below.

if( (your timer).isActive ) return;

huangapple
  • 本文由 发表于 2023年6月22日 18:18:39
  • 转载请务必保留本文链接:https://go.coder-hub.com/76530873.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定