whitebear
whitebear

Reputation: 12433

Behaivor of instances new-ed from the same StatefulWidget

I have troubled with the behaivor of instances from the same class.

I made two instances of StatefulWidget(BodyLayout) class. and switch them by BottomNavigationBar

But only one of initState() of BodyLayout is called.

I am confused by this behavior , State is shared each instances???

I want to each initState() is called separately.

Please help some hint.

These are full source code below.

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      routes: {
        "/": (_) => new MyHomePage(),
        "/browser": (_) => new Text("not use"),
      }
    );
  }
}
class Article{
  String title;
  String url;
  Article();
}
class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title; 
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  List<Widget> _myLayouts = [];
  int _currentIndex = 0;

  @override
  void initState() {
    super.initState();
    _myLayouts = [
      new BodyLayout("latest"),
      new BodyLayout("pop"),
    ];
  }
  void _onItemTapped(int index) {
    print("itemTapped :" +index.toString());
    setState(() {
      _currentIndex = index; 
    });
  }
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
      //  title: Text(widget.title),
      ),
      body: _myLayouts[_currentIndex],

      bottomNavigationBar: BottomNavigationBar(
      items: const <BottomNavigationBarItem>[
        BottomNavigationBarItem(
          icon: Icon(Icons.home),
          title: Text('latest'),
        ),
        BottomNavigationBarItem(
          icon: Icon(Icons.business),
          title: Text('pop'),
        ),
      ],
        currentIndex: _currentIndex,
        selectedItemColor: Colors.amber[800],
        onTap: _onItemTapped,
      ),
    );
  }
}


class BodyLayout extends StatefulWidget {
  final String mode;
  BodyLayout(this.mode);

  @override
  _BodyLayoutState createState() => _BodyLayoutState();
}
class _BodyLayoutState extends State<BodyLayout>{
  List<Article> articles = [];
  bool loading = true;
  int page = 1;
  @override 
  void initState(){
    super.initState();
    print ("init:" + widget.mode);// this called only one time.....
    _callApi(); // this called only one time.....
  }

  void _callApi() {
    var a = Article();
    a.title = widget.mode;
    a.url = widget.mode;
    articles.add(a);
    setState((){
      loading = false;
    });
  }
  @override
  Widget build(BuildContext context) {
    if(loading) {
      return CircularProgressIndicator();
    }
    return ListView.builder(
      itemCount: articles.length,
      itemBuilder: (context, index) {
        return ListTile(
          title: Text(articles[index].title),
        );
      },
    );
  }
}

Upvotes: 1

Views: 26

Answers (1)

Igor Kharakhordin
Igor Kharakhordin

Reputation: 9903

I am confused by this behavior , State is shared each instances???

Yes, it is. You need to provide unique keys to your widgets:

class BodyLayout extends StatefulWidget {
  BodyLayout(this.mode, {Key key}) : super(key: key);

...
    _myLayouts = [
      new BodyLayout("latest", key: Key('1')),
      new BodyLayout("pop", key: Key('2')),
    ];

and bam - it works:

I/flutter (12871): init:latest
I/flutter (12871): itemTapped :1
I/flutter (12871): init:pop

From the docs:

A StatefulWidget keeps the same State object when moving from one location in the tree to another if its creator used a GlobalKey for its key.

So basically, if your widgets have the same key (or don't have one), they're interpreted as the same widget.

Upvotes: 2

Related Questions