如何在Flutter中以编程方式更改main.dart的AppBar标题?

5

我在main.dart中有一个AppBar,我想将其定义为子元素的主要内容,但是在子页面上时,我想要改变AppBar本身的标题,我应该如何正确地实现这一点呢?

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

class MyApp extends StatelessWidget{
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: "Flutter App",
      theme: ThemeData(
        primaryColor: Colors.cyan,
        brightness: Brightness.dark
      ),
      home: Scaffold(
        appBar: AppBar(
          title: Text("Main Dart"),
        ),
        body: HomeScreen(),
      ),
      routes: <String, WidgetBuilder>{
        '/homeScreen': (buildContext)=>HomeScreen(),
        '/second': (buildContext)=>Second() 
      },
    );
  }
}

//HomeScreen or Second Widget on different dart file

class HomeScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    //here I want to change the title of Main Dart to HomeScreen
    return Container(
      child: Center(
        child: FlatButton(
          child: new Text("Home screen"),
          onPressed: (){
            Route route = MaterialPageRoute(builder: (context) => Second());
            Navigator.push(context, route);
          },
        ),
      ),
    );
  }
}

我需要在每个屏幕上使用Scaffold(appBar:AppBar(...),...)吗?这是最好的方法吗?


你能发布 Second 类吗?你在每个路由中都使用 Scaffold 吗? - Doc
@Doc 我想避免在每个路由上使用 Scaffold,所以我的 second 路由和 HomeScreen 相同。 - flix
是否完全不可避免要使用脚手架? - Doc
1
@doc 我脑海中的主要问题是:“为什么我需要在每个页面上放置'Scaffold'才能更改'appBar'的标题?”这只是我作为初学者Flutter开发人员的问题,欢迎任何建议。 - flix
我已经发布了我的示例代码,基于您只想更改每个页面的标题而不是Scaffold工作的想法。后者将需要在每个页面中单独使用Scaffold。 - Doc
显示剩余7条评论
5个回答

6
app_properties_bloc.dart中创建一个BLoC以处理应用程序属性。
final appBloc = AppPropertiesBloc();

class AppPropertiesBloc{
  StreamController<String> _title = StreamController<String>();

  Stream<String> get titleStream => _title.stream;

  updateTitle(String newTitle){
    _title.sink.add(newTitle);
  }

  dispose() {
    _title.close();
  }
}

像这样在AppBar中使用流构建器:

AppBar(
   title: StreamBuilder<Object>(
       stream: appBloc.titleStream,
       initialData: "Main Dart",
       builder: (context, snapshot) {
           return Text(snapshot.data);
       }
   ),
),

使用此方法在按钮的 onPressed() 事件中更新标题。

onPressed: () {
    appBloc.updateTitle('new title');
},

11
为什么这看起来像是用重武器杀一只苍蝇? - Doc
1
对于一个简单的任务来说,这太过繁琐了。我还以为Flutter会减少代码量 :( - Doc
这是因为你只有一个像这样变化的属性,对于这个属性,你觉得它不值得这样做。但是假设你有多个类似的属性,那么你就不会有同样的感觉了。使用StreamBuilder来更新数据是高效的,因为它只会构建需要更新的小部件。 - Kalpesh Kundanani
是的,我也有同样的想法,如果有很多东西那就很好,但对于只有一件事来说,看起来有点过度了。 - Doc
@KalpeshKundanani 在 Navigator.of(context).pop(); 的情况下会起作用吗? - Hemant Kaushik
@HemantKaushik,要使此功能正常工作,您只需要调用“appBloc.updateTitle('new title');”。因此,在导航时,如果您可以调用此方法,则它将起作用。 - Kalpesh Kundanani

2
如果您只更改 Scaffold 的标题,则以下代码将适用。
我正在使用每个屏幕提供的标题创建一个 DefaultScaffold。在这里,代码将显示 MainPage 和另外两个具有更改标题的相同 AppBar 的页面。
void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(initialRoute: 'home', routes: <String, WidgetBuilder>{
      'home': (context) => SOMain(),
      '/secondPage': (context) => DefaultScaffold("Second Screen", SOSecond()),
      '/thirdPage': (context) => DefaultScaffold("Third Screen", SOThird()),
    });
  }
}

class DefaultScaffold extends StatelessWidget {
  String title;
  Widget body;

  DefaultScaffold(this.title, this.body);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(title),
      ),
      body: body,
    );
  }
}

class SOMain extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return DefaultScaffold(
      "Main Screen",
      Center(
        child: RaisedButton(
            child: Text("Go to second screen"),
            onPressed: () {
              Navigator.pushNamed(context, '/secondPage');
            }),
      ),
    );
  }
}

class SOSecond extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Center(
      child: RaisedButton(
        child: Text("Go the 3rd screen"),
        onPressed: () => Navigator.pushNamed(context, "/thirdPage"),
      ),
    );
  }
}

class SOThird extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Center(child: Text("You are on last screen"));
  }
}

注意:这只是一种简单的解决方法,可能不是最佳选择。

Original Answer翻译成"最初的回答"


0

这里有些答案太复杂了。以下是一个使用应用栏更新子级并带有脚手架小部件的完整工作示例。

您可以在Dart Pad中运行示例

import 'package:flutter/material.dart';

void main() {
  runApp(const MyHomePage(title: 'init title'));
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});
  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  final ValueNotifier<String?> _appBarTitleNotifier = ValueNotifier<String?>(null);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: ValueListenableBuilder<String?>(
            builder: (BuildContext context, String? value, Widget? child) {
              return Text(value ?? widget.title);
            },
            valueListenable: _appBarTitleNotifier,
          ),
        ),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              ChildDemoTitleBar(titleNotifier: _appBarTitleNotifier)
            ],
          ),
        ),
      ),
    );
  }
}

class ChildDemoTitleBar extends StatefulWidget {
  final ValueNotifier<String?> titleNotifier;

  const ChildDemoTitleBar({Key? key, required this.titleNotifier})
      : super(key: key);

  @override
  State<ChildDemoTitleBar> createState() => _ChildDemoTitleBarState();
}

class _ChildDemoTitleBarState extends State<ChildDemoTitleBar> {
  int _counter = 0;

  @override
  Widget build(BuildContext context) {
    return Padding(
        padding: const EdgeInsets.fromLTRB(20, 0, 20, 20),
        child: InkWell(
            onTap: () {
              _counter++;
              widget.titleNotifier.value = "title updated $_counter";
            },
            child: const Text("tap to update title")));
  }
}

0
您可以通过使用回调函数实现从子类更新父类parent的状态。
父类:
import 'package:flutter/material.dart';

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

class ParentState extends State<Parent> {

  String title = "Old Title";

  @override
  Widget build(BuildContext context) {

    return new Scaffold(
        appBar: new AppBar(
        title: new Text(title),
      ),
      body: DaysFragmentView(onTitleSelect: (String value) {
            setTitle(value);
        }
      ),
    );

  }

  void setTitle(String value) {
    setState(() {
      title = value;
    });
  }
}

子类


typedef TitleCallback = void Function(Title color);

class DaysFragmentView extends StatelessWidget {
  const DaysFragmentView({this.onTitleSelect});

  final TitleCallback onTitleSelect;

  @override
  Widget build(BuildContext context) {
    return Row(
      children: <Widget>[
        RaisedButton(
          child: Text('One'),
          onPressed: () {
            onTitleSelect("TITLE ONE");
          },
        ),
        RaisedButton(
          child: Text('Two'),
          onPressed: () {
            onTitleSelect("TITLE TWO");
          },
        )
      ],
    );
  }
}

参考:


0

使用ValueListenableBuilder是一种选择。

使用实例变量

String appTitle;

然后将应用栏设置为以下代码块中所示:

appBar: AppBar(
          ValueListenableBuilder<String>(
          valueListenable: appTitle,
          builder: (context, value, child) {
            return Text(appTitle.value);
          },
        ),

之后,您可以在其他类中简单地设置appTitle.value。标题也会更改,因为它会监听该值。

appTitle.value = "Home Screen";

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接