Home > OS >  When state of widget is created
When state of widget is created

Time:06-26

I have two tabA, tabB and these are switched by user tapping.

What I want to do is

  1. At first tabA opens.
  2. tabB text changes depending on tabA state.
  3. user opens tabB,there comes changed text.

My basic idea is getting the tabB state via object key.

However there is a problem,

Before opening tabB, state of tabB is not created.

These are my source code tabA is playerPage, tabB is settingPage

class _MainFrameState extends State<MainFrame>{
  void _onItemTapped(int index) => setState(() => _selectedIndex = index );
  int _selectedIndex = 0;

  Widget settingPage = Text("");
  Widget playerPage = Text("");

  GlobalKey<_PlayerPageState> _playerPageKey = new GlobalKey<_PlayerPageState>();
  GlobalKey<_SettingPageState> _settingPageKey = new GlobalKey<_SettingPageState>();
  
  @override
  void initState() {
    print("Main Frame init state");
    super.initState();
    playerPage = PlayerPage(key:_playerPageKey);
    settingPage = SettingPage(key:_settingPageKey);


  }
  function whenSomethingChanged(){ //this is called by push button or some user action,so initState() is already called.
      print(playerPage.currentState!) // it has state and operatable.
      print(settingPage.currentState!)  // I want to change the tabB text but it returns null.

  }
  @override
  Widget build(BuildContext context) {
    print("Main Frame build");
    
    List<Widget> _pages = [
      playerPage,
      settingPage
    ];
    return Scaffold(
      
      appBar: EmptyAppBar(),
      body:Container(
        decoration: new BoxDecoration(color: Colors.red),
        child:Center(child:_pages.elementAt(_selectedIndex),)),
        
      bottomNavigationBar: BottomNavigationBar(
        selectedItemColor: Color(0xffC03410),
        unselectedItemColor: Color(0xffE96D2F),
          backgroundColor: Color(0xffF7BF51),
          type: BottomNavigationBarType.fixed,
        items: const <BottomNavigationBarItem>[
          BottomNavigationBarItem(
            icon: Icon(Icons.play_circle),
            label: 'Player',
          ),
          BottomNavigationBarItem(
            icon: Icon(Icons.settings),
            label: 'Setting',
          ),
        ],
        currentIndex: _selectedIndex,
        onTap: _onItemTapped,
      ),
    );
  }
}

I can tell that when first build is called PlayerPage is created but SettingPage is not.

However I want to control the SettinPage , before it show.

What is the solution for this problem??

CodePudding user response:

Since your settingPage is not in the tree, you can't really access its state because it was not created.

Whenever you change your _selectedIndex in the code below, either a new settingPage or a playerPage is inflated, so just create it already with the value it depends on, listening to it if necessary.

      body:Container(
        decoration: new BoxDecoration(color: Colors.red),
        child:Center(child:_pages.elementAt(_selectedIndex),)),

CodePudding user response:

Since you are updating the screen based on the users input in page 1 I have included a code where if the text field is empty it will show a message and if it has value that value is displayed in second page

import 'dart:math';

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: MainFrame(),
    );
  }
}

class MainFrame extends StatefulWidget {
  const MainFrame({key});

  @override
  State<MainFrame> createState() => _MainFrameState();
}

class _MainFrameState extends State<MainFrame> {
  void _onItemTapped(int index) => setState(() {
        print(playerPageController.text);
        _selectedIndex = index;
      });
  int _selectedIndex = 0;
  late TextEditingController playerPageController;

  late Widget settingPage;
  late Widget playerPage;
  late List<Widget> _pages;
  @override
  void initState() {
    playerPageController = TextEditingController();
    print("Main Frame init state");
    super.initState();
  }


  @override
  Widget build(BuildContext context) {
    settingPage = Text((playerPageController.text.isEmpty)
        ? "Theres no content here"
        : playerPageController.text);
    playerPage = Center(
        child: TextField(
      controller: playerPageController,
      autofocus: true,
    ));
    _pages = [playerPage, settingPage];
    print("Main Frame build");

    return Scaffold(
      body: Container(
          decoration: new BoxDecoration(color: Colors.red),
          child: Center(
            child: _pages.elementAt(_selectedIndex),
          )),
      bottomNavigationBar: BottomNavigationBar(
        selectedItemColor: Color(0xffC03410),
        unselectedItemColor: Color(0xffE96D2F),
        backgroundColor: Color(0xffF7BF51),
        type: BottomNavigationBarType.fixed,
        items: const <BottomNavigationBarItem>[
          BottomNavigationBarItem(
            icon: Icon(Icons.play_circle),
            label: 'Player',
          ),
          BottomNavigationBarItem(
            icon: Icon(Icons.settings),
            label: 'Setting',
          ),
        ],
        currentIndex: _selectedIndex,
        onTap: _onItemTapped,
      ),
    );
  }
}

CodePudding user response:

Since you didn't tell what kind of state of TabA you want to show on TabB, I will assume it's a String and wrote this. There's a lot of ways of doing what you want to achieve, this is just one of them.

Step 1: Pass the value of PlayerPage state to SettingPage like this:

class SettingPage extends StatefulWidget {
  const SettingPage({
    Key? key,
    required this.text,
  }) : super(key: key);

  final String text;

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

class _SettingPageState extends State<SettingPage> {
  @override
  Widget build(BuildContext context) {
    return Center(child: Text(widget.text));
  }
}

Step 2: Let's assume state you want to show on SettingPage is from TextField. Maybe a player name. Then you'll need to pass that textField value to MainFrame() widget whenever it changes.

class PlayerPage extends StatefulWidget {
  const PlayerPage({
    Key? key,
    required this.onTextChanged,
  }) : super(key: key);

  final Function(String) onTextChanged;

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

class _PlayerPagetate extends State<PlayerPage> {
  final TextEditingController _controller = TextEditingController();

  @override
  Widget build(BuildContext context) {
    return Center(
      child: TextField(
        controller: _controller,
        onChanged: (value) {
          widget.onTextChanged(value);
        },
      ),
    );
  }
}

Step 3: On MainFrame widget, now you get playerName into _playerName variable from PlayerPlage and pass it into SettingPage like this. Whenever the value changes _playerName will change too:

class _MainFrameState extends State<MainFrame> {
  void _onItemTapped(int index) => setState(() => _selectedIndex = index);
  int _selectedIndex = 0;
  String _playerName = '';

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

  @override
  Widget build(BuildContext context) {
    print("Main Frame build");

    List<Widget> _pages = [
      PlayerPage(onTextChanged: (value) {
        setState(() {
          _playerName = value;
        });
      }),
      SettingPage(text: _playerName)
    ];

    return Scaffold(
      // rest of the codes are same.
    )
  • Related