簡體   English   中英

從另一個有狀態小部件調用一個有狀態小部件中的方法 - Flutter

[英]call method in one stateful widget from another stateful widget - Flutter

我有一個正在處理的顫振項目,我不能把整個代碼放在 500 多行代碼中,所以我會嘗試像我使用 imp 一樣簡單地提出我的問題。 部分代碼。

我有一個有狀態的小部件,並且在擴展State<MusicPlayer>的類下的有狀態小部件中有一些功能

文件lib\\main.dart

只需采取一個簡單的功能,如

class MyAppState extends State<MyApp>{
...
void printSample (){
  print("Sample text");
}
...

此函數位於主類中的有狀態小部件內。

還有另一個文件lib\\MyApplication.dart

這個文件還有一個有狀態的小部件,我可以做些什么,以便我可以在這里調用函數printSample() ..

class MyApplicationState extends State<MyApplication>{
...
@override
  Widget build(BuildContext context) {
    return new FlatButton(
      child: new Text("Print Sample Text"),
      onPressed :(){
       // i want to cal the function here how is it possible to call the 
       // function 
       // printSample()  from here??  
      }
    );
  }
...
}

要調用父函數,您可以使用回調模式。 在這個例子中,一個函數 ( onColorSelected ) 被傳遞給孩子。 當按下按鈕時,孩子會調用該函數:

import 'package:flutter/material.dart';

class Parent extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return ParentState();
  }
}

class ParentState extends State<Parent> {
  Color selectedColor = Colors.grey;

  @override
  Widget build(BuildContext context) {
    return Column(
      children: <Widget>[
        Container(
          color: selectedColor,
          height: 200.0,
        ),
        ColorPicker(
          onColorSelect: (Color color) {
            setState(() {
              selectedColor = color;
            });
          },
        )
      ],
    );
  }
}

class ColorPicker extends StatelessWidget {
  const ColorPicker({this.onColorSelect});

  final ColorCallback onColorSelect;

  @override
  Widget build(BuildContext context) {
    return Row(
      children: <Widget>[
        RaisedButton(
          child: Text('red'),
          color: Colors.red,
          onPressed: () {
            onColorSelect(Colors.red);
          },
        ),
        RaisedButton(
          child: Text('green'),
          color: Colors.green,
          onPressed: () {
            onColorSelect(Colors.green);
          },
        ),
        RaisedButton(
          child: Text('blue'),
          color: Colors.blue,
          onPressed: () {
            onColorSelect(Colors.blue);
          },
        )
      ],
    );
  }
}

typedef ColorCallback = void Function(Color color);

內部 Flutter 小部件(如按鈕或表單字段)使用完全相同的模式。 如果您只想調用不帶任何參數的函數,您可以使用VoidCallback類型來代替定義您自己的回調類型。


如果您想通知更高級別的父母,您可以在每個層次結構級別重復此模式:

class ColorPickerWrapper extends StatelessWidget {
  const ColorPickerWrapper({this.onColorSelect});

  final ColorCallback onColorSelect;

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: EdgeInsets.all(20.0),
      child: ColorPicker(onColorSelect: onColorSelect),
    )
  }
}

Flutter 不鼓勵從父小部件調用子小部件的方法。 相反,Flutter 鼓勵您將子項的狀態作為構造函數參數傳遞。 您只需在父小部件中調用setState來更新其子部件,而不是調用子部件的方法。


一種替代方法是 Flutter 中的controller類( ScrollControllerAnimationController ,...)。 這些也作為構造函數參數傳遞給子級,它們包含控制子級狀態的方法,而無需在父級上調用setState 例子:

scrollController.animateTo(200.0, duration: Duration(seconds: 1), curve: Curves.easeInOut);

然后要求孩子們聆聽這些變化以更新他們的內部狀態。 當然,你也可以實現自己的控制器類。 如果需要,我建議您查看 Flutter 的源代碼以了解其工作原理。


期貨和流是傳遞狀態的另一種選擇,也可用於調用子函數。

但我真的不推薦它。 如果你需要調用子widget的方法,那很像是你的應用架構有缺陷。 嘗試將狀態向上移動到共同祖先!

您可以通過使用小部件的來做到這一點

myWidget.dart

    class MyWidget extends StatefulWidget {
     
      const MyWidget ({Key key}) : super(key: key);
     
      @override
      State<StatefulWidget> createState()=> MyState();
    
  
    }

   class MyState extends State<MyWidget >{
        
          
               
      Widget build(BuildContext context){ return ....}
    
      void printSample (){
         print("Sample text");
      }
        
 }

現在,當使用進myWidget聲明GlobalKey作為全球關鍵

  GlobalKey<MyState> _myKey = GlobalKey();

並在創建小部件時傳遞它

MyWidget(
key : _myKey,
)

通過這個鍵,你可以調用 state 內的任何公共方法

_myKey.currentState.printSample();

如果你想調用 printSample() func 你可以使用:

class Myapp extends StatefulWidget{
...
    MyappState myAppState=new MyappState();
    @override
    MyappState createState() => myAppState;
    void printSample(){
        myAppState.printSample();
    }
}
class MyAppState extends State<MyApp>{
    void printSample (){
        print("Sample text");
    }
}

...............
Myapp _myapp = new Myapp();
myapp.printSample();
...

我通過反復試驗找到了另一個解決方案,但它奏效了。

import 'main.dart' as main;

然后在 onPressed 下添加這一行。

main.MyAppState().printSample();

您可以嘗試一下,它會從Page1 ( StatefulWidget ) 小部件調用Page2 ( StatefulWidget ) 中定義的方法。

class Page1 extends StatefulWidget {
  @override
  _Page1State createState() => _Page1State();
}

class _Page1State extends State<Page1> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: RaisedButton(
          child: Text("Call page 2 method"),
          onPressed: () => Page2().method(),
        ),
      ),
    );
  }
}

class Page2 extends StatefulWidget {
  method() => createState().methodInPage2();

  @override
  _Page2State createState() => _Page2State();
}

class _Page2State extends State<Page2> {
  methodInPage2() => print("method in page 2");

  @override
  Widget build(BuildContext context) => Container();
}

雖然使用回調和GlobalKey對簡單的用例很好,但對於更復雜的設置,它們可能應該被視為反模式,因為它們掛鈎到小部件類型並依賴於低級實現邏輯。

如果您發現自己添加了越來越多的回調/全局鍵,並且開始變得混亂,那么可能是時候切換到StreamController + StreamSubscription類的東西了。 通過這種方式,您可以將事件與特定的小部件類型分離,並抽象出小部件間的通信邏輯。

注冊一個事件控制器

在您的頂級小部件(應用程序級或頁面級,取決於您的需要)中創建StreamController實例,並確保在dispose()方法中釋放它:

class _TopLevelPageState extends State<TopLevelPage> {
  StreamController<MyCustomEventType> eventController = StreamController<MyCustomEventType>.broadcast();

// ...
  @override
  void dispose() {
    eventController.close();
    super.dispose();
  }
}

eventController實例作為構造函數參數傳遞給任何需要偵聽事件和/或觸發事件的子小部件。

MyCustomEventType可以是一個枚舉(如果您不需要傳遞額外的數據)或一個帶有任何您需要的字段的常規對象,以防您需要在事件上設置額外的數據。

觸發事件

現在,在任何小部件(包括您聲明StreamController的父小部件)中,您都可以通過以下方式觸發事件:

eventController.sink.add(MyCustomEventType.UserLoginIsComplete);

監聽事件

要在您的孩子(或父小部件)中設置偵聽器,請將以下代碼放入initState()


class _ChildWidgetState extends State<ChildWidget> {
  @override
  void initState() {
    super.initState();
    // NOTE: 'widget' is the ootb reference to the `ChildWidget` instance. 
    this.eventSubscription = widget.eventController.stream.asBroadcastStream().listen((event) {
      if (event == MyCustomEventType.UserLoginIsComplete) {
        print('handling LOGIN COMPLETE event ' + event.toString());
      } else {
        print('handling some other event ' + event.toString());
      }
  }

  @override
  void dispose() {
    this.parentSubscription.cancel();
    super.dispose();
  }
}

請注意,如果您覆蓋StreamController.done()則您的偵聽器將不會觸發,因為done()替換了您之前設置的任何偵聽器。


注意:如果您在兩個小部件之間有一對一的通信關系,那么您不需要廣播事件風格——在這種情況下,您可以創建沒有.broadcast()的控制器,即使用StreamController<MyCustomEventType>()並你可以使用.stream.listen()而不是.stream.asBroadcastStream().listen() .stream.listen() 另見https://api.dart.dev/stable/dart-async/Stream-class.html

有關概述此方法和其他方法的答案,請參閱Inter Widget 通信

這里 HomePage 是父頁面, ChildPage 是子頁面。 有一種方法叫做 onSelectItem,我們需要從子頁面調用它。

class HomePage extends StatefulWidget {

  @override HomePageState createState() => HomePageState();
}

class HomePageState extends State<HomePage> {

  onSelectItem(String param) {
    print(param);
  }

  @override Widget build(BuildContext context) {

  }
}

class ChildPage extends StatefulWidget {
  final HomePageState homePageState;

  ChildPage({Key key, @required this.homePageState}) : super(key: key);

  _ChildPageState createState() => _ChildPageState();
}

class _ChildPageState extends State<ChildPage> {
  @override Widget build(BuildContext context) {

    return RaisedButton(
      onPressed: () {
        widget.homePageState.onSelectItem("test");
      },
      child: const Text(
          'Click here',
          style: TextStyle(fontSize: 20)
      ),
    );
  }
}

因此,通過使用小部件父類狀態,我們可以調用父類方法。

我剛剛為這個問題找到了一個最簡單的解決方案

顯然,您只需創建一個僅包含一個方法的文件,您就可以直接調用它

例如,我想在名為custom_show_bottom_sheet.dart文件中創建一個 showModalBottomSheet 方法:

import 'package:flutter/material.dart';

customShowBottomSheet(context, Widget child){
    showModalBottomSheet(
      isScrollControlled: true,
      context: context,
      builder: (BuildContext context) {
        return Wrap(
          children: <Widget>[
            child,
          ],
        );
      });
}

你可以簡單地這樣稱呼它:

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

class TestScreen extends StatelessWidget {
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: FloatingActionButton(
            onPressed: (){
              customShowBottomSheet( //A method is called here
                  context,
                  Container(
                    height: 200,
                    child: Text("This is a test"),
                  )
              );
            }),
      ),
    );
  }
}

希望這會有所幫助! 如果我誤解了您的問題或任何內容,請告訴我。

我正在開發一個撲朔迷離的項目,我無法將整個代碼引起其超過500行的代碼,因此我將嘗試問我的問題,就像使用amp的icn一樣簡單。 代碼部分。

我有一個有狀態的小部件,並且在有狀態的小部件內的某些類下,擴展了類,擴展了State<MusicPlayer>

文件lib\\main.dart

只是采取一個簡單的功能,如

class MyAppState extends State<MyApp>{
...
void printSample (){
  print("Sample text");
}
...

此函數位於主類內部的有狀態窗口小部件中。

還有另一個文件lib\\MyApplication.dart

這個文件也有一個有狀態的小部件,我可以做些什么,以便在這里調用函數printSample()

class MyApplicationState extends State<MyApplication>{
...
@override
  Widget build(BuildContext context) {
    return new FlatButton(
      child: new Text("Print Sample Text"),
      onPressed :(){
       // i want to cal the function here how is it possible to call the 
       // function 
       // printSample()  from here??  
      }
    );
  }
...
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM